commit 953496fc74f035ed2f4b31670b09e62f4a5f950f
parent 01636786be26be6524cb685e9eecfa9479572f50
Author: kernelkind <kernelkind@gmail.com>
Date: Tue, 29 Apr 2025 12:39:54 -0400
note media: unnest full screen media
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
1 file changed, 181 insertions(+), 169 deletions(-)
diff --git a/crates/notedeck_ui/src/note/media.rs b/crates/notedeck_ui/src/note/media.rs
@@ -1,5 +1,7 @@
+use std::collections::HashMap;
+
use egui::{Button, Color32, Image, Response, Sense, Window};
-use notedeck::{Images, MediaCacheType};
+use notedeck::{GifState, Images, MediaCacheType, TexturedImage};
use crate::{
gif::{handle_repaint, retrieve_latest_texture},
@@ -86,179 +88,189 @@ pub(crate) fn image_carousel(
});
if show_popup {
- let current_image = current_image
- .as_ref()
- .expect("the image was actually clicked");
- let image = current_image.clone().0;
- let cache_type = current_image.clone().1;
-
- Window::new("image_popup")
- .title_bar(false)
- .fixed_size(ui.ctx().screen_rect().size())
- .fixed_pos(ui.ctx().screen_rect().min)
- .frame(egui::Frame::NONE)
- .show(ui.ctx(), |ui| {
- ui.centered_and_justified(|ui| {
- render_images(
- ui,
- img_cache,
- &image,
- ImageType::Content,
- cache_type,
- |_| {},
- |_, _| {},
- |ui, url, renderable_media, gifs| {
- let screen_rect = ui.ctx().screen_rect();
-
- // escape
- if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
- ui.ctx().memory_mut(|mem| {
- mem.data.insert_temp(carousel_id.with("show_popup"), false);
- });
- }
-
- // background
- ui.painter().rect_filled(
- screen_rect,
- 0.0,
- Color32::from_black_alpha(230),
- );
-
- // zoom init
- let zoom_id = carousel_id.with("zoom_level");
- let mut zoom = ui
- .ctx()
- .memory(|mem| mem.data.get_temp(zoom_id).unwrap_or(1.0_f32));
-
- // pan init
- let pan_id = carousel_id.with("pan_offset");
- let mut pan_offset = ui.ctx().memory(|mem| {
- mem.data.get_temp(pan_id).unwrap_or(egui::Vec2::ZERO)
- });
-
- // zoom & scroll
- if ui.input(|i| i.pointer.hover_pos()).is_some() {
- let scroll_delta = ui.input(|i| i.smooth_scroll_delta);
- if scroll_delta.y != 0.0 {
- let zoom_factor =
- if scroll_delta.y > 0.0 { 1.05 } else { 0.95 };
- zoom *= zoom_factor;
- zoom = zoom.clamp(0.1, 5.0);
-
- if zoom <= 1.0 {
- pan_offset = egui::Vec2::ZERO;
- }
+ if let Some((image_url, cache_type)) = current_image {
+ show_full_screen_media(ui, &image_url, cache_type, img_cache, carousel_id);
+ }
+ }
+}
- ui.ctx().memory_mut(|mem| {
- mem.data.insert_temp(zoom_id, zoom);
- mem.data.insert_temp(pan_id, pan_offset);
- });
- }
- }
-
- let texture = handle_repaint(
- ui,
- retrieve_latest_texture(&image, gifs, renderable_media),
- );
-
- let texture_size = texture.size_vec2();
- let screen_size = screen_rect.size();
- let scale = (screen_size.x / texture_size.x)
- .min(screen_size.y / texture_size.y)
- .min(1.0);
- let scaled_size = texture_size * scale * zoom;
-
- let visible_width = scaled_size.x.min(screen_size.x);
- let visible_height = scaled_size.y.min(screen_size.y);
-
- let max_pan_x = ((scaled_size.x - visible_width) / 2.0).max(0.0);
- let max_pan_y = ((scaled_size.y - visible_height) / 2.0).max(0.0);
-
- if max_pan_x > 0.0 {
- pan_offset.x = pan_offset.x.clamp(-max_pan_x, max_pan_x);
- } else {
- pan_offset.x = 0.0;
- }
-
- if max_pan_y > 0.0 {
- pan_offset.y = pan_offset.y.clamp(-max_pan_y, max_pan_y);
- } else {
- pan_offset.y = 0.0;
- }
-
- let (rect, response) = ui.allocate_exact_size(
- egui::vec2(visible_width, visible_height),
- egui::Sense::click_and_drag(),
- );
-
- let uv_min = egui::pos2(
- 0.5 - (visible_width / scaled_size.x) / 2.0
- + pan_offset.x / scaled_size.x,
- 0.5 - (visible_height / scaled_size.y) / 2.0
- + pan_offset.y / scaled_size.y,
- );
-
- let uv_max = egui::pos2(
- uv_min.x + visible_width / scaled_size.x,
- uv_min.y + visible_height / scaled_size.y,
- );
-
- let uv = egui::Rect::from_min_max(uv_min, uv_max);
-
- ui.painter()
- .image(texture.id(), rect, uv, egui::Color32::WHITE);
- let img_rect = ui.allocate_rect(rect, Sense::click());
-
- if img_rect.clicked() {
- ui.ctx().memory_mut(|mem| {
- mem.data.insert_temp(carousel_id.with("show_popup"), true);
- });
- } else if img_rect.clicked_elsewhere() {
- ui.ctx().memory_mut(|mem| {
- mem.data.insert_temp(carousel_id.with("show_popup"), false);
- });
- }
-
- // Handle dragging for pan
- if response.dragged() {
- let delta = response.drag_delta();
-
- pan_offset.x -= delta.x;
- pan_offset.y -= delta.y;
-
- if max_pan_x > 0.0 {
- pan_offset.x = pan_offset.x.clamp(-max_pan_x, max_pan_x);
- } else {
- pan_offset.x = 0.0;
- }
+fn show_full_screen_media(
+ ui: &mut egui::Ui,
+ image_url: &str,
+ cache_type: MediaCacheType,
+ img_cache: &mut Images,
+ carousel_id: egui::Id,
+) {
+ Window::new("image_popup")
+ .title_bar(false)
+ .fixed_size(ui.ctx().screen_rect().size())
+ .fixed_pos(ui.ctx().screen_rect().min)
+ .frame(egui::Frame::NONE)
+ .show(ui.ctx(), |ui| {
+ ui.centered_and_justified(|ui| {
+ render_images(
+ ui,
+ img_cache,
+ image_url,
+ ImageType::Content,
+ cache_type,
+ |_| {},
+ |_, _| {},
+ |ui, url, renderable_media, gifs| {
+ render_full_screen_media(ui, renderable_media, gifs, url, carousel_id);
+ },
+ );
+ });
+ });
+}
- if max_pan_y > 0.0 {
- pan_offset.y = pan_offset.y.clamp(-max_pan_y, max_pan_y);
- } else {
- pan_offset.y = 0.0;
- }
+fn render_full_screen_media(
+ ui: &mut egui::Ui,
+ renderable_media: &mut TexturedImage,
+ gifs: &mut HashMap<String, GifState>,
+ image_url: &str,
+ carousel_id: egui::Id,
+) {
+ let screen_rect = ui.ctx().screen_rect();
- ui.ctx().memory_mut(|mem| {
- mem.data.insert_temp(pan_id, pan_offset);
- });
- }
-
- // reset zoom on double-click
- if response.double_clicked() {
- pan_offset = egui::Vec2::ZERO;
- zoom = 1.0;
- ui.ctx().memory_mut(|mem| {
- mem.data.insert_temp(pan_id, pan_offset);
- mem.data.insert_temp(zoom_id, zoom);
- });
- }
-
- copy_link(url, response);
- },
- );
- });
+ // escape
+ if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
+ ui.ctx().memory_mut(|mem| {
+ mem.data.insert_temp(carousel_id.with("show_popup"), false);
+ });
+ }
+
+ // background
+ ui.painter()
+ .rect_filled(screen_rect, 0.0, Color32::from_black_alpha(230));
+
+ // zoom init
+ let zoom_id = carousel_id.with("zoom_level");
+ let mut zoom = ui
+ .ctx()
+ .memory(|mem| mem.data.get_temp(zoom_id).unwrap_or(1.0_f32));
+
+ // pan init
+ let pan_id = carousel_id.with("pan_offset");
+ let mut pan_offset = ui
+ .ctx()
+ .memory(|mem| mem.data.get_temp(pan_id).unwrap_or(egui::Vec2::ZERO));
+
+ // zoom & scroll
+ if ui.input(|i| i.pointer.hover_pos()).is_some() {
+ let scroll_delta = ui.input(|i| i.smooth_scroll_delta);
+ if scroll_delta.y != 0.0 {
+ let zoom_factor = if scroll_delta.y > 0.0 { 1.05 } else { 0.95 };
+ zoom *= zoom_factor;
+ zoom = zoom.clamp(0.1, 5.0);
+
+ if zoom <= 1.0 {
+ pan_offset = egui::Vec2::ZERO;
+ }
+
+ ui.ctx().memory_mut(|mem| {
+ mem.data.insert_temp(zoom_id, zoom);
+ mem.data.insert_temp(pan_id, pan_offset);
});
+ }
}
+
+ let texture = handle_repaint(
+ ui,
+ retrieve_latest_texture(image_url, gifs, renderable_media),
+ );
+
+ let texture_size = texture.size_vec2();
+ let screen_size = screen_rect.size();
+ let scale = (screen_size.x / texture_size.x)
+ .min(screen_size.y / texture_size.y)
+ .min(1.0);
+ let scaled_size = texture_size * scale * zoom;
+
+ let visible_width = scaled_size.x.min(screen_size.x);
+ let visible_height = scaled_size.y.min(screen_size.y);
+
+ let max_pan_x = ((scaled_size.x - visible_width) / 2.0).max(0.0);
+ let max_pan_y = ((scaled_size.y - visible_height) / 2.0).max(0.0);
+
+ if max_pan_x > 0.0 {
+ pan_offset.x = pan_offset.x.clamp(-max_pan_x, max_pan_x);
+ } else {
+ pan_offset.x = 0.0;
+ }
+
+ if max_pan_y > 0.0 {
+ pan_offset.y = pan_offset.y.clamp(-max_pan_y, max_pan_y);
+ } else {
+ pan_offset.y = 0.0;
+ }
+
+ let (rect, response) = ui.allocate_exact_size(
+ egui::vec2(visible_width, visible_height),
+ egui::Sense::click_and_drag(),
+ );
+
+ let uv_min = egui::pos2(
+ 0.5 - (visible_width / scaled_size.x) / 2.0 + pan_offset.x / scaled_size.x,
+ 0.5 - (visible_height / scaled_size.y) / 2.0 + pan_offset.y / scaled_size.y,
+ );
+
+ let uv_max = egui::pos2(
+ uv_min.x + visible_width / scaled_size.x,
+ uv_min.y + visible_height / scaled_size.y,
+ );
+
+ let uv = egui::Rect::from_min_max(uv_min, uv_max);
+
+ ui.painter()
+ .image(texture.id(), rect, uv, egui::Color32::WHITE);
+ let img_rect = ui.allocate_rect(rect, Sense::click());
+
+ if img_rect.clicked() {
+ ui.ctx().memory_mut(|mem| {
+ mem.data.insert_temp(carousel_id.with("show_popup"), true);
+ });
+ } else if img_rect.clicked_elsewhere() {
+ ui.ctx().memory_mut(|mem| {
+ mem.data.insert_temp(carousel_id.with("show_popup"), false);
+ });
+ }
+
+ // Handle dragging for pan
+ if response.dragged() {
+ let delta = response.drag_delta();
+
+ pan_offset.x -= delta.x;
+ pan_offset.y -= delta.y;
+
+ if max_pan_x > 0.0 {
+ pan_offset.x = pan_offset.x.clamp(-max_pan_x, max_pan_x);
+ } else {
+ pan_offset.x = 0.0;
+ }
+
+ if max_pan_y > 0.0 {
+ pan_offset.y = pan_offset.y.clamp(-max_pan_y, max_pan_y);
+ } else {
+ pan_offset.y = 0.0;
+ }
+
+ ui.ctx().memory_mut(|mem| {
+ mem.data.insert_temp(pan_id, pan_offset);
+ });
+ }
+
+ // reset zoom on double-click
+ if response.double_clicked() {
+ pan_offset = egui::Vec2::ZERO;
+ zoom = 1.0;
+ ui.ctx().memory_mut(|mem| {
+ mem.data.insert_temp(pan_id, pan_offset);
+ mem.data.insert_temp(zoom_id, zoom);
+ });
+ }
+
+ copy_link(image_url, response);
}
fn copy_link(url: &str, img_resp: Response) {