image viewer popup when clicking an image in a message

This commit is contained in:
Green Sky 2025-03-17 22:52:31 +01:00
parent 10ad2be8bf
commit 90a28d727b
No known key found for this signature in database
GPG Key ID: DBE05085D874AB4A
7 changed files with 425 additions and 312 deletions

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

@ -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();
}

@ -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", &current_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", &current_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;