image viewer popup when clicking an image in a message
This commit is contained in:
parent
10ad2be8bf
commit
90a28d727b
12
flake.lock
generated
12
flake.lock
generated
@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -37,11 +37,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1720691131,
|
||||
"narHash": "sha256-CWT+KN8aTPyMIx8P303gsVxUnkinIz0a/Cmasz1jyIM=",
|
||||
"lastModified": 1735651292,
|
||||
"narHash": "sha256-YLbzcBtYo1/FEzFsB3AnM16qFc6fWPMIoOuSoDwvg9g=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a046c1202e11b62cbede5385ba64908feb7bfac4",
|
||||
"rev": "0da3c44a9460a26d2025ec3ed2ec60a895eb1114",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -91,6 +91,8 @@ target_sources(tomato PUBLIC
|
||||
./chat_gui/contact_list.cpp
|
||||
./chat_gui/file_selector.hpp
|
||||
./chat_gui/file_selector.cpp
|
||||
./chat_gui/image_viewer_popup.hpp
|
||||
./chat_gui/image_viewer_popup.cpp
|
||||
./chat_gui/send_image_popup.hpp
|
||||
./chat_gui/send_image_popup.cpp
|
||||
./chat_gui/settings_window.hpp
|
||||
|
66
src/chat_gui/image_viewer_popup.cpp
Normal file
66
src/chat_gui/image_viewer_popup.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "./image_viewer_popup.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
ImageViewerPopup::ImageViewerPopup(MessageTextureCache& mtc) : _mtc(mtc) {
|
||||
}
|
||||
|
||||
// open popup with (image) file
|
||||
void ImageViewerPopup::view(Message3Handle m) {
|
||||
if (static_cast<bool>(_m)) {
|
||||
std::cout << "IVP warning: overriding open image\n";
|
||||
}
|
||||
|
||||
_m = m;
|
||||
|
||||
_open_popup = true;
|
||||
}
|
||||
|
||||
// call this each frame
|
||||
void ImageViewerPopup::render(float) {
|
||||
if (_open_popup) {
|
||||
_open_popup = false;
|
||||
ImGui::OpenPopup("Image##ImageViewerPopup");
|
||||
}
|
||||
|
||||
if (!ImGui::BeginPopup("Image##ImageViewerPopup", ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
_m = {}; // meh, event on close would be nice, but the reset is cheap
|
||||
_scale = 1.f;
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SliderFloat("scale", &_scale, 0.05f, 2.f);
|
||||
|
||||
auto [id, img_width, img_height] = _mtc.get(_m);
|
||||
|
||||
img_width = std::max<int32_t>(5, _scale * img_width);
|
||||
img_height = std::max<int32_t>(5, _scale * img_height);
|
||||
|
||||
ImGui::Image(
|
||||
id,
|
||||
ImVec2{
|
||||
static_cast<float>(img_width),
|
||||
static_cast<float>(img_height)
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: figure out nice scroll zooming
|
||||
//ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY);
|
||||
//const auto prev_scale = _scale;
|
||||
//_scale += ImGui::GetIO().MouseWheel * 0.05f;
|
||||
//_scale = std::clamp(_scale, 0.05f, 3.f);
|
||||
|
||||
//if (std::abs(prev_scale - _scale) > 0.001f) {
|
||||
//}
|
||||
|
||||
if (ImGui::Shortcut(ImGuiKey_Escape)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
_m = {};
|
||||
_scale = 1.f;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
32
src/chat_gui/image_viewer_popup.hpp
Normal file
32
src/chat_gui/image_viewer_popup.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <solanaceae/message3/registry_message_model.hpp>
|
||||
|
||||
#include "./texture_cache_defs.hpp"
|
||||
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/handle.hpp>
|
||||
|
||||
struct ImageViewerPopup {
|
||||
MessageTextureCache& _mtc;
|
||||
|
||||
Message3Handle _m{};
|
||||
float _scale {1.f};
|
||||
|
||||
bool _open_popup {false};
|
||||
|
||||
//void reset(void);
|
||||
|
||||
public:
|
||||
ImageViewerPopup(MessageTextureCache& mtc);
|
||||
|
||||
// open popup with (image) message
|
||||
//void view(ObjectHandle o);
|
||||
void view(Message3Handle m);
|
||||
|
||||
// call this each frame
|
||||
void render(float time_delta);
|
||||
|
||||
// TODO: events (destroy/update)
|
||||
};
|
||||
|
@ -219,339 +219,346 @@ void SendImagePopup::render(float time_delta) {
|
||||
}
|
||||
|
||||
// TODO: add cancel shortcut (esc)
|
||||
if (ImGui::BeginPopupModal("send image##SendImagePopup", nullptr/*, ImGuiWindowFlags_NoDecoration*/)) {
|
||||
//const auto TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const auto TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
if (!ImGui::BeginPopupModal("send image##SendImagePopup", nullptr/*, ImGuiWindowFlags_NoDecoration*/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
preview_image.doAnimation(getTimeMS());
|
||||
//const auto TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const auto TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
time += time_delta;
|
||||
time = fmod(time, 1.f); // fract()
|
||||
preview_image.doAnimation(getTimeMS());
|
||||
|
||||
//ImGui::Text("send file....\n......");
|
||||
time += time_delta;
|
||||
time = fmod(time, 1.f); // fract()
|
||||
|
||||
{
|
||||
float width = ImGui::GetWindowContentRegionMax().x - ImGui::GetWindowContentRegionMin().x;
|
||||
float height = crop_rect.h * (width / crop_rect.w);
|
||||
if (cropping) {
|
||||
height = original_image.height * (width / original_image.width);
|
||||
//ImGui::Text("send file....\n......");
|
||||
|
||||
{
|
||||
float width = ImGui::GetWindowContentRegionMax().x - ImGui::GetWindowContentRegionMin().x;
|
||||
float height = crop_rect.h * (width / crop_rect.w);
|
||||
if (cropping) {
|
||||
height = original_image.height * (width / original_image.width);
|
||||
}
|
||||
|
||||
const float max_height =
|
||||
ImGui::GetWindowContentRegionMax().y
|
||||
- (
|
||||
ImGui::GetWindowContentRegionMin().y
|
||||
+ TEXT_BASE_HEIGHT*(2-1) // row of buttons (-1 bc fh inclues fontsize)
|
||||
+ ImGui::GetFrameHeightWithSpacing()*4
|
||||
)
|
||||
;
|
||||
if (height > max_height) {
|
||||
width *= max_height / height;
|
||||
height = max_height;
|
||||
}
|
||||
|
||||
// TODO: propergate texture id type
|
||||
|
||||
// save curser pos
|
||||
const auto pre_img_curser_screen = ImGui::GetCursorScreenPos();
|
||||
const auto pre_img_curser = ImGui::GetCursorPos();
|
||||
|
||||
if (cropping) { // if cropping
|
||||
// display full image
|
||||
ImGui::Image(
|
||||
preview_image.getID<ImTextureID>(),
|
||||
ImVec2{static_cast<float>(width), static_cast<float>(height)}
|
||||
);
|
||||
const auto post_img_curser = ImGui::GetCursorPos();
|
||||
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.5f, 0.5f, 0.5f, 0.2f});
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.5f, 0.5f, 0.5f, 0.4f});
|
||||
|
||||
auto ul_clipper_pos = ImVec2{float(crop_rect.x)/original_image.width, float(crop_rect.y)/original_image.height};
|
||||
{ // crop upper left clipper
|
||||
|
||||
ImGui::SetCursorPos({
|
||||
pre_img_curser.x + ul_clipper_pos.x * width,
|
||||
pre_img_curser.y + ul_clipper_pos.y * height
|
||||
});
|
||||
|
||||
ImGui::Button("##ul_clipper", {TEXT_BASE_HEIGHT, TEXT_BASE_HEIGHT});
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (dragging_last_frame_ul) {
|
||||
auto drag_total = ImGui::GetMouseDragDelta();
|
||||
drag_total.x = (drag_total.x / width) * original_image.width;
|
||||
drag_total.y = (drag_total.y / height) * original_image.height;
|
||||
|
||||
crop_rect.x = std::max<float>(crop_before_drag.x + drag_total.x, 0.01f);
|
||||
crop_rect.y = std::max<float>(crop_before_drag.y + drag_total.y, 0.01f);
|
||||
|
||||
crop_rect.x = std::min<int32_t>(crop_rect.x, original_image.width-2);
|
||||
crop_rect.y = std::min<int32_t>(crop_rect.y, original_image.height-2);
|
||||
|
||||
crop_rect.w = crop_before_drag.w - (crop_rect.x - crop_before_drag.x);
|
||||
crop_rect.h = crop_before_drag.h - (crop_rect.y - crop_before_drag.y);
|
||||
} else {
|
||||
if (ImGui::IsItemActive()) {
|
||||
dragging_last_frame_ul = true;
|
||||
// drag started on button, start drag
|
||||
}
|
||||
}
|
||||
} else if (dragging_last_frame_ul) { // was dragging
|
||||
dragging_last_frame_ul = false;
|
||||
crop_before_drag = crop_rect;
|
||||
}
|
||||
}
|
||||
|
||||
const float max_height =
|
||||
ImGui::GetWindowContentRegionMax().y
|
||||
- (
|
||||
ImGui::GetWindowContentRegionMin().y
|
||||
+ TEXT_BASE_HEIGHT*(2-1) // row of buttons (-1 bc fh inclues fontsize)
|
||||
+ ImGui::GetFrameHeightWithSpacing()*4
|
||||
)
|
||||
;
|
||||
if (height > max_height) {
|
||||
width *= max_height / height;
|
||||
height = max_height;
|
||||
auto lr_clipper_pos = ImVec2{float(crop_rect.x+crop_rect.w)/original_image.width, float(crop_rect.y+crop_rect.h)/original_image.height};
|
||||
{ // crop lower right clipper
|
||||
ImGui::SetCursorPos({
|
||||
pre_img_curser.x + lr_clipper_pos.x * width - TEXT_BASE_HEIGHT,
|
||||
pre_img_curser.y + lr_clipper_pos.y * height - TEXT_BASE_HEIGHT
|
||||
});
|
||||
|
||||
ImGui::Button("##lr_clipper", {TEXT_BASE_HEIGHT, TEXT_BASE_HEIGHT});
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (dragging_last_frame_lr) {
|
||||
auto drag_total = ImGui::GetMouseDragDelta();
|
||||
drag_total.x = (drag_total.x / width) * original_image.width;
|
||||
drag_total.y = (drag_total.y / height) * original_image.height;
|
||||
|
||||
crop_rect.w = std::min<float>(crop_before_drag.w + drag_total.x, original_image.width);
|
||||
crop_rect.h = std::min<float>(crop_before_drag.h + drag_total.y, original_image.height);
|
||||
} else {
|
||||
if (ImGui::IsItemActive()) {
|
||||
dragging_last_frame_lr = true;
|
||||
// drag started on button, start drag
|
||||
}
|
||||
}
|
||||
} else if (dragging_last_frame_lr) { // was dragging
|
||||
dragging_last_frame_lr = false;
|
||||
crop_before_drag = crop_rect;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: propergate texture id type
|
||||
// sanitzie after tool
|
||||
crop_rect = sanitizeCrop(crop_rect, original_image.width, original_image.height);
|
||||
|
||||
// save curser pos
|
||||
const auto pre_img_curser_screen = ImGui::GetCursorScreenPos();
|
||||
const auto pre_img_curser = ImGui::GetCursorPos();
|
||||
|
||||
if (cropping) { // if cropping
|
||||
// display full image
|
||||
ImGui::Image(
|
||||
preview_image.getID<ImTextureID>(),
|
||||
ImVec2{static_cast<float>(width), static_cast<float>(height)}
|
||||
);
|
||||
const auto post_img_curser = ImGui::GetCursorPos();
|
||||
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.5f, 0.5f, 0.5f, 0.2f});
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.5f, 0.5f, 0.5f, 0.4f});
|
||||
|
||||
auto ul_clipper_pos = ImVec2{float(crop_rect.x)/original_image.width, float(crop_rect.y)/original_image.height};
|
||||
{ // crop upper left clipper
|
||||
|
||||
ImGui::SetCursorPos({
|
||||
pre_img_curser.x + ul_clipper_pos.x * width,
|
||||
pre_img_curser.y + ul_clipper_pos.y * height
|
||||
});
|
||||
|
||||
ImGui::Button("##ul_clipper", {TEXT_BASE_HEIGHT, TEXT_BASE_HEIGHT});
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (dragging_last_frame_ul) {
|
||||
auto drag_total = ImGui::GetMouseDragDelta();
|
||||
drag_total.x = (drag_total.x / width) * original_image.width;
|
||||
drag_total.y = (drag_total.y / height) * original_image.height;
|
||||
|
||||
crop_rect.x = std::max<float>(crop_before_drag.x + drag_total.x, 0.01f);
|
||||
crop_rect.y = std::max<float>(crop_before_drag.y + drag_total.y, 0.01f);
|
||||
|
||||
crop_rect.x = std::min<int32_t>(crop_rect.x, original_image.width-2);
|
||||
crop_rect.y = std::min<int32_t>(crop_rect.y, original_image.height-2);
|
||||
|
||||
crop_rect.w = crop_before_drag.w - (crop_rect.x - crop_before_drag.x);
|
||||
crop_rect.h = crop_before_drag.h - (crop_rect.y - crop_before_drag.y);
|
||||
} else {
|
||||
if (ImGui::IsItemActive()) {
|
||||
dragging_last_frame_ul = true;
|
||||
// drag started on button, start drag
|
||||
{ // 4 lines delimiting the crop result
|
||||
ImU32 line_color = 0xffffffff;
|
||||
{ // calc color
|
||||
auto rgb = [](float x) -> ImVec4 {
|
||||
auto f = [](float x) {
|
||||
while (x < 0.f) {
|
||||
x += 1.f;
|
||||
}
|
||||
}
|
||||
} else if (dragging_last_frame_ul) { // was dragging
|
||||
dragging_last_frame_ul = false;
|
||||
crop_before_drag = crop_rect;
|
||||
}
|
||||
}
|
||||
|
||||
auto lr_clipper_pos = ImVec2{float(crop_rect.x+crop_rect.w)/original_image.width, float(crop_rect.y+crop_rect.h)/original_image.height};
|
||||
{ // crop lower right clipper
|
||||
ImGui::SetCursorPos({
|
||||
pre_img_curser.x + lr_clipper_pos.x * width - TEXT_BASE_HEIGHT,
|
||||
pre_img_curser.y + lr_clipper_pos.y * height - TEXT_BASE_HEIGHT
|
||||
});
|
||||
x = std::fmod(x, 1.f); // fract()
|
||||
|
||||
ImGui::Button("##lr_clipper", {TEXT_BASE_HEIGHT, TEXT_BASE_HEIGHT});
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (dragging_last_frame_lr) {
|
||||
auto drag_total = ImGui::GetMouseDragDelta();
|
||||
drag_total.x = (drag_total.x / width) * original_image.width;
|
||||
drag_total.y = (drag_total.y / height) * original_image.height;
|
||||
|
||||
crop_rect.w = std::min<float>(crop_before_drag.w + drag_total.x, original_image.width);
|
||||
crop_rect.h = std::min<float>(crop_before_drag.h + drag_total.y, original_image.height);
|
||||
} else {
|
||||
if (ImGui::IsItemActive()) {
|
||||
dragging_last_frame_lr = true;
|
||||
// drag started on button, start drag
|
||||
if (x < 1.f/3) {
|
||||
return x * 3;
|
||||
} else if (x < 2.f/3) {
|
||||
return (1 - (x - (1.f/3))) * 3 - 2;
|
||||
} else {
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
} else if (dragging_last_frame_lr) { // was dragging
|
||||
dragging_last_frame_lr = false;
|
||||
crop_before_drag = crop_rect;
|
||||
}
|
||||
}
|
||||
|
||||
// sanitzie after tool
|
||||
crop_rect = sanitizeCrop(crop_rect, original_image.width, original_image.height);
|
||||
|
||||
{ // 4 lines delimiting the crop result
|
||||
ImU32 line_color = 0xffffffff;
|
||||
{ // calc color
|
||||
auto rgb = [](float x) -> ImVec4 {
|
||||
auto f = [](float x) {
|
||||
while (x < 0.f) {
|
||||
x += 1.f;
|
||||
}
|
||||
|
||||
x = std::fmod(x, 1.f); // fract()
|
||||
|
||||
if (x < 1.f/3) {
|
||||
return x * 3;
|
||||
} else if (x < 2.f/3) {
|
||||
return (1 - (x - (1.f/3))) * 3 - 2;
|
||||
} else {
|
||||
return 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
float red = f(x);
|
||||
float green = f(x - (1.f/3));
|
||||
float blue = f(x - (2.f/3));
|
||||
|
||||
return {red, green, blue, 1.f};
|
||||
};
|
||||
|
||||
line_color = ImGui::GetColorU32(rgb(time));
|
||||
}
|
||||
float red = f(x);
|
||||
float green = f(x - (1.f/3));
|
||||
float blue = f(x - (2.f/3));
|
||||
|
||||
// x vertical
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x + ul_clipper_pos.x * width, pre_img_curser_screen.y},
|
||||
{pre_img_curser_screen.x + ul_clipper_pos.x * width, pre_img_curser_screen.y + height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
return {red, green, blue, 1.f};
|
||||
};
|
||||
|
||||
// y horizontal
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x, pre_img_curser_screen.y + ul_clipper_pos.y * height},
|
||||
{pre_img_curser_screen.x + width, pre_img_curser_screen.y + ul_clipper_pos.y * height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
|
||||
// w vertical
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x + lr_clipper_pos.x * width, pre_img_curser_screen.y},
|
||||
{pre_img_curser_screen.x + lr_clipper_pos.x * width, pre_img_curser_screen.y + height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
|
||||
// h horizontal
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x, pre_img_curser_screen.y + lr_clipper_pos.y * height},
|
||||
{pre_img_curser_screen.x + width, pre_img_curser_screen.y + lr_clipper_pos.y * height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
line_color = ImGui::GetColorU32(rgb(time));
|
||||
}
|
||||
|
||||
// cancel/ok buttons in the img center?
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ImGui::SetCursorPos(post_img_curser);
|
||||
} else {
|
||||
crop_rect = sanitizeCrop(crop_rect, original_image.width, original_image.height);
|
||||
|
||||
// display cropped area
|
||||
ImGui::Image(
|
||||
preview_image.getID<ImTextureID>(),
|
||||
ImVec2{static_cast<float>(width), static_cast<float>(height)},
|
||||
ImVec2{float(crop_rect.x)/original_image.width, float(crop_rect.y)/original_image.height},
|
||||
ImVec2{float(crop_rect.x+crop_rect.w)/original_image.width, float(crop_rect.y+crop_rect.h)/original_image.height}
|
||||
// x vertical
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x + ul_clipper_pos.x * width, pre_img_curser_screen.y},
|
||||
{pre_img_curser_screen.x + ul_clipper_pos.x * width, pre_img_curser_screen.y + height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
|
||||
// transparent crop button on image
|
||||
#if 0
|
||||
const auto post_img_curser = ImGui::GetCursorPos();
|
||||
// y horizontal
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x, pre_img_curser_screen.y + ul_clipper_pos.y * height},
|
||||
{pre_img_curser_screen.x + width, pre_img_curser_screen.y + ul_clipper_pos.y * height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
|
||||
ImGui::SetCursorPos(pre_img_curser);
|
||||
// w vertical
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x + lr_clipper_pos.x * width, pre_img_curser_screen.y},
|
||||
{pre_img_curser_screen.x + lr_clipper_pos.x * width, pre_img_curser_screen.y + height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
|
||||
// TODO: fancy cropping toggle
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.5f, 0.5f, 0.5f, 0.2f});
|
||||
if (ImGui::Button("crop", {TEXT_BASE_WIDTH*8, TEXT_BASE_HEIGHT*2})) {
|
||||
cropping = true;
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPos(post_img_curser);
|
||||
#endif
|
||||
// h horizontal
|
||||
ImGui::GetWindowDrawList()->AddLine(
|
||||
{pre_img_curser_screen.x, pre_img_curser_screen.y + lr_clipper_pos.y * height},
|
||||
{pre_img_curser_screen.x + width, pre_img_curser_screen.y + lr_clipper_pos.y * height},
|
||||
line_color,
|
||||
1.f
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const bool cropped = crop_rect.x != 0 || crop_rect.y != 0 || crop_rect.w != original_image.width || crop_rect.h != original_image.height;
|
||||
if (cropping) {
|
||||
if (ImGui::Button("done")) {
|
||||
cropping = false;
|
||||
}
|
||||
// cancel/ok buttons in the img center?
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ImGui::SetCursorPos(post_img_curser);
|
||||
} else {
|
||||
if (ImGui::Button("crop")) {
|
||||
crop_rect = sanitizeCrop(crop_rect, original_image.width, original_image.height);
|
||||
|
||||
// display cropped area
|
||||
ImGui::Image(
|
||||
preview_image.getID<ImTextureID>(),
|
||||
ImVec2{static_cast<float>(width), static_cast<float>(height)},
|
||||
ImVec2{float(crop_rect.x)/original_image.width, float(crop_rect.y)/original_image.height},
|
||||
ImVec2{float(crop_rect.x+crop_rect.w)/original_image.width, float(crop_rect.y+crop_rect.h)/original_image.height}
|
||||
);
|
||||
|
||||
// transparent crop button on image
|
||||
#if 0
|
||||
const auto post_img_curser = ImGui::GetCursorPos();
|
||||
|
||||
ImGui::SetCursorPos(pre_img_curser);
|
||||
|
||||
// TODO: fancy cropping toggle
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.5f, 0.5f, 0.5f, 0.2f});
|
||||
if (ImGui::Button("crop", {TEXT_BASE_WIDTH*8, TEXT_BASE_HEIGHT*2})) {
|
||||
cropping = true;
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPos(post_img_curser);
|
||||
#endif
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("reset")) {
|
||||
crop_rect.x = 0;
|
||||
crop_rect.y = 0;
|
||||
crop_rect.w = original_image.width;
|
||||
crop_rect.h = original_image.height;
|
||||
crop_before_drag = crop_rect;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("x:%d y:%d w:%d h:%d", crop_rect.x, crop_rect.y, crop_rect.w, crop_rect.h);
|
||||
|
||||
bool recalc_size = false;
|
||||
if (cropped) {
|
||||
if (!compress) {
|
||||
// looks like a change
|
||||
recalc_size = true;
|
||||
}
|
||||
compress = true;
|
||||
}
|
||||
|
||||
recalc_size |= ImGui::Checkbox("compress", &compress);
|
||||
if (cropped && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("required since cropped!");
|
||||
}
|
||||
|
||||
static int current_compressor = 0;
|
||||
|
||||
if (compress) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Combo("##compression_type", ¤t_compressor, "webp\0jpeg\0png\0qoi\0");
|
||||
|
||||
ImGui::Indent();
|
||||
// combo "webp""webp-lossless""png""jpg?"
|
||||
// if lossy quality slider (1-100) default 80
|
||||
if (current_compressor == 0 || current_compressor == 1) {
|
||||
const uint32_t qmin = 1;
|
||||
const uint32_t qmax = 100;
|
||||
recalc_size |= ImGui::SliderScalar("quality", ImGuiDataType_U32, &quality, &qmin, &qmax);
|
||||
}
|
||||
if (current_compressor == 2) {
|
||||
const uint32_t qmin = 0;
|
||||
const uint32_t qmax = 9;
|
||||
recalc_size |= ImGui::SliderScalar("compression_level", ImGuiDataType_U32, &compression_level, &qmin, &qmax);
|
||||
}
|
||||
|
||||
if (recalc_size) {
|
||||
// compress and save temp? cooldown? async? save size only?
|
||||
// print size where?
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
//if (ImGui::Button("X cancel", {ImGui::GetWindowContentRegionWidth()/2.f, TEXT_BASE_HEIGHT*2})) {
|
||||
if (ImGui::Button("X cancel", {ImGui::GetContentRegionAvail().x/2.f, TEXT_BASE_HEIGHT*2})) {
|
||||
_on_cancel();
|
||||
ImGui::CloseCurrentPopup();
|
||||
reset();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("send ->", {-FLT_MIN, TEXT_BASE_HEIGHT*2})) {
|
||||
if (compress || cropped) {
|
||||
// TODO: copy bad
|
||||
ImageLoaderI::ImageResult tmp_img;
|
||||
if (cropped) {
|
||||
std::cout << "SIP: CROP!!!!!\n";
|
||||
tmp_img = original_image.crop(
|
||||
crop_rect.x,
|
||||
crop_rect.y,
|
||||
crop_rect.w,
|
||||
crop_rect.h
|
||||
);
|
||||
} else {
|
||||
tmp_img = original_image;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> new_data;
|
||||
|
||||
// HACK: generic list
|
||||
if (current_compressor == 0) {
|
||||
new_data = ImageEncoderWebP{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".webp");
|
||||
}
|
||||
} else if (current_compressor == 1) {
|
||||
new_data = ImageEncoderSTBJpeg{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});;
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".jpg");
|
||||
}
|
||||
} else if (current_compressor == 2) {
|
||||
new_data = ImageEncoderSTBPNG{}.encodeToMemoryRGBA(tmp_img, {{"png_compression_level", compression_level}});;
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".png");
|
||||
}
|
||||
} else if (current_compressor == 3) {
|
||||
new_data = ImageEncoderQOI{}.encodeToMemoryRGBA(tmp_img, {});;
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".qoi");
|
||||
}
|
||||
}
|
||||
// error
|
||||
|
||||
} else {
|
||||
_on_send(original_data, original_file_ext);
|
||||
}
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
reset();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
const bool cropped = crop_rect.x != 0 || crop_rect.y != 0 || crop_rect.w != original_image.width || crop_rect.h != original_image.height;
|
||||
if (cropping) {
|
||||
if (ImGui::Button("done")) {
|
||||
cropping = false;
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button("crop")) {
|
||||
cropping = true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("reset")) {
|
||||
crop_rect.x = 0;
|
||||
crop_rect.y = 0;
|
||||
crop_rect.w = original_image.width;
|
||||
crop_rect.h = original_image.height;
|
||||
crop_before_drag = crop_rect;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("x:%d y:%d w:%d h:%d", crop_rect.x, crop_rect.y, crop_rect.w, crop_rect.h);
|
||||
|
||||
bool recalc_size = false;
|
||||
if (cropped) {
|
||||
if (!compress) {
|
||||
// looks like a change
|
||||
recalc_size = true;
|
||||
}
|
||||
compress = true;
|
||||
}
|
||||
|
||||
recalc_size |= ImGui::Checkbox("compress", &compress);
|
||||
if (cropped && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("required since cropped!");
|
||||
}
|
||||
|
||||
static int current_compressor = 0;
|
||||
|
||||
if (compress) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Combo("##compression_type", ¤t_compressor, "webp\0jpeg\0png\0qoi\0");
|
||||
|
||||
ImGui::Indent();
|
||||
// combo "webp""webp-lossless""png""jpg?"
|
||||
// if lossy quality slider (1-100) default 80
|
||||
if (current_compressor == 0 || current_compressor == 1) {
|
||||
const uint32_t qmin = 1;
|
||||
const uint32_t qmax = 100;
|
||||
recalc_size |= ImGui::SliderScalar("quality", ImGuiDataType_U32, &quality, &qmin, &qmax);
|
||||
}
|
||||
if (current_compressor == 2) {
|
||||
const uint32_t qmin = 0;
|
||||
const uint32_t qmax = 9;
|
||||
recalc_size |= ImGui::SliderScalar("compression_level", ImGuiDataType_U32, &compression_level, &qmin, &qmax);
|
||||
}
|
||||
|
||||
if (recalc_size) {
|
||||
// compress and save temp? cooldown? async? save size only?
|
||||
// print size where?
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
//if (ImGui::Button("X cancel", {ImGui::GetWindowContentRegionWidth()/2.f, TEXT_BASE_HEIGHT*2})) {
|
||||
if (ImGui::Button("X cancel", {ImGui::GetContentRegionAvail().x/2.f, TEXT_BASE_HEIGHT*2})) {
|
||||
_on_cancel();
|
||||
ImGui::CloseCurrentPopup();
|
||||
reset();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("send ->", {-FLT_MIN, TEXT_BASE_HEIGHT*2})) {
|
||||
if (compress || cropped) {
|
||||
// TODO: copy bad
|
||||
ImageLoaderI::ImageResult tmp_img;
|
||||
if (cropped) {
|
||||
std::cout << "SIP: CROP!!!!!\n";
|
||||
tmp_img = original_image.crop(
|
||||
crop_rect.x,
|
||||
crop_rect.y,
|
||||
crop_rect.w,
|
||||
crop_rect.h
|
||||
);
|
||||
} else {
|
||||
tmp_img = original_image;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> new_data;
|
||||
|
||||
// HACK: generic list
|
||||
if (current_compressor == 0) {
|
||||
new_data = ImageEncoderWebP{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".webp");
|
||||
}
|
||||
} else if (current_compressor == 1) {
|
||||
new_data = ImageEncoderSTBJpeg{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});;
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".jpg");
|
||||
}
|
||||
} else if (current_compressor == 2) {
|
||||
new_data = ImageEncoderSTBPNG{}.encodeToMemoryRGBA(tmp_img, {{"png_compression_level", compression_level}});;
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".png");
|
||||
}
|
||||
} else if (current_compressor == 3) {
|
||||
new_data = ImageEncoderQOI{}.encodeToMemoryRGBA(tmp_img, {});;
|
||||
if (!new_data.empty()) {
|
||||
_on_send(new_data, ".qoi");
|
||||
}
|
||||
}
|
||||
// error
|
||||
|
||||
} else {
|
||||
_on_send(original_data, original_file_ext);
|
||||
}
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
reset();
|
||||
}
|
||||
|
||||
if (ImGui::Shortcut(ImGuiKey_Escape)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
reset();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,8 @@ ChatGui4::ChatGui4(
|
||||
_msg_tc(msg_tc),
|
||||
_b_tc(_bil, tu),
|
||||
_theme(theme),
|
||||
_sip(tu)
|
||||
_sip(tu),
|
||||
_ivp(_msg_tc)
|
||||
{
|
||||
_os_sr.subscribe(ObjectStore_Event::object_update);
|
||||
}
|
||||
@ -220,6 +221,7 @@ ChatGui4::~ChatGui4(void) {
|
||||
float ChatGui4::render(float time_delta, bool window_hidden, bool window_focused) {
|
||||
_fss.render();
|
||||
_sip.render(time_delta);
|
||||
_ivp.render(time_delta);
|
||||
_b_tc.update();
|
||||
_b_tc.workLoadQueue();
|
||||
|
||||
@ -1330,7 +1332,9 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
||||
{0.5f, 0.5f, 0.5f, 0.8f} // border
|
||||
);
|
||||
|
||||
// TODO: clickable to open in internal image viewer
|
||||
if (ImGui::IsItemClicked()) {
|
||||
_ivp.view(Message3Handle{reg, e});
|
||||
}
|
||||
}
|
||||
} else if (o.all_of<ObjComp::F::SingleInfo>()) { // only show info if not inlined image
|
||||
// just filename
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "./bitset_image_loader.hpp"
|
||||
#include "./chat_gui/file_selector.hpp"
|
||||
#include "./chat_gui/send_image_popup.hpp"
|
||||
#include "./chat_gui/image_viewer_popup.hpp"
|
||||
|
||||
#include <entt/container/dense_map.hpp>
|
||||
|
||||
@ -39,6 +40,7 @@ class ChatGui4 : public ObjectStoreEventI {
|
||||
|
||||
FileSelector _fss;
|
||||
SendImagePopup _sip;
|
||||
ImageViewerPopup _ivp;
|
||||
|
||||
// TODO: refactor this to allow multiple open contacts
|
||||
std::optional<Contact4> _selected_contact;
|
||||
|
Loading…
x
Reference in New Issue
Block a user