Skip to content

Commit f0cecc1

Browse files
committed
Added Image detail for Image Viewer
1 parent 816fae5 commit f0cecc1

File tree

8 files changed

+565
-143
lines changed

8 files changed

+565
-143
lines changed

src/app.rs

Lines changed: 97 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use makepad_widgets::{image_cache::ImageError, makepad_micro_serde::*, *};
1010
use matrix_sdk::ruma::{OwnedRoomId, RoomId};
1111
use crate::{
1212
avatar_cache::clear_avatar_cache, home::{
13-
main_desktop_ui::MainDesktopUiAction, new_message_context_menu::NewMessageContextMenuWidgetRefExt, room_screen::{MessageAction, clear_timeline_states}, rooms_list::{RoomsListAction, RoomsListRef, RoomsListUpdate, clear_all_invited_rooms, enqueue_rooms_list_update}
13+
main_desktop_ui::MainDesktopUiAction, new_message_context_menu::NewMessageContextMenuWidgetRefExt, room_image_message_detail::RoomImageMessageDetailWidgetRefExt, room_screen::{MessageAction, clear_timeline_states}, rooms_list::{RoomsListAction, RoomsListRef, RoomsListUpdate, clear_all_invited_rooms, enqueue_rooms_list_update}
1414
}, join_leave_room_modal::{
1515
JoinLeaveModalKind, JoinLeaveRoomModalAction, JoinLeaveRoomModalWidgetRefExt
1616
}, login::login_screen::LoginAction, logout::logout_confirm_modal::{LogoutAction, LogoutConfirmModalAction, LogoutConfirmModalWidgetRefExt}, persistence, profile::user_profile_cache::clear_user_profile_cache, room::BasicRoomDetails, shared::{callout_tooltip::{
@@ -41,6 +41,7 @@ live_design! {
4141
use crate::shared::callout_tooltip::CalloutTooltip;
4242
use crate::shared::image_viewer::ImageViewer;
4343
use crate::shared::icon_button::RobrixIconButton;
44+
use crate::home::room_image_message_detail::RoomImageMessageDetail;
4445
use link::tsp_link::TspVerificationModal;
4546

4647

@@ -98,33 +99,23 @@ live_design! {
9899
}
99100

100101
image_viewer = <Modal> {
101-
102102
content: {
103103
width: Fill, height: Fill,
104104
flow: Down
105105
show_bg: true
106106
draw_bg: {
107107
color: #000
108108
}
109+
109110
<View> {
110111
width: Fill, height: Fill,
111112
flow: Overlay
112113
image_viewer_inner = <ImageViewer> {
113114
align: {x: 0.5, y: 0.5}
114-
debug: true
115115
padding: {bottom: 0}
116116
}
117-
image_detail = <View> {
118-
width: 500, height: 200,
119-
debug: false
120-
image_viewer_status_label = <Label> {
121-
width: Fit, height: 30,
122-
text: "Loading----- image...",
123-
draw_text: {
124-
text_style: <REGULAR_TEXT>{font_size: 14},
125-
color: (COLOR_PRIMARY)
126-
}
127-
}
117+
image_detail = <RoomImageMessageDetail> {
118+
width: Fill, height: Fill,
128119
}
129120
}
130121

@@ -134,6 +125,7 @@ live_design! {
134125
padding: 10
135126
align: {x: 0.5, y: 0.8}
136127
spacing: 10
128+
137129
image_viewer_loading_spinner_view = <View> {
138130
width: Fit, height: Fit
139131
loading_spinner = <LoadingSpinner> {
@@ -459,47 +451,8 @@ impl MatchEvent for App {
459451
_ => {}
460452
}
461453

462-
match action.downcast_ref() {
463-
Some(ImageViewerAction::Show(load_state)) => {
464-
match &load_state {
465-
&LoadState::Loading(thumbnail_data) => {
466-
self.ui.view(id!(image_viewer_loading_spinner_view)).set_visible(cx, true);
467-
self.ui.label(id!(image_viewer_status_label)).set_text(cx, "Loading...");
468-
let _ = self.ui.image_viewer(id!(image_viewer_inner)).display_rotated_image(cx, &thumbnail_data);
469-
}
470-
&LoadState::Loaded(image_bytes) => {
471-
self.ui.view(id!(image_viewer_loading_spinner_view)).set_visible(cx, false);
472-
let _ = self.ui.image_viewer(id!(image_viewer_inner)).display_rotated_image(cx, &image_bytes);
473-
if let Err(error) = self.ui.image_viewer(id!(image_viewer_inner)).display_image(cx, &image_bytes) {
474-
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, true);
475-
let err = match error {
476-
ImageError::JpgDecode(_) | ImageError::PngDecode(_) => ImageViewerError::UnsupportedFormat,
477-
ImageError::EmptyData => ImageViewerError::BadData,
478-
ImageError::PathNotFound(_) => ImageViewerError::NotFound,
479-
ImageError::UnsupportedFormat => ImageViewerError::UnsupportedFormat,
480-
_ => ImageViewerError::BadData,
481-
};
482-
self.ui.label(id!(image_viewer_status_label)).set_text(cx, image_viewer_error_to_string(&err));
483-
} else {
484-
self.ui.view(id!(zoom_button_view)).set_visible(cx, true);
485-
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, false);
486-
self.ui.label(id!(image_viewer_status_label)).set_text(cx, "");
487-
}
488-
}
489-
&LoadState::Error(error) => {
490-
self.ui.view(id!(image_viewer_loading_spinner_view)).set_visible(cx, false);
491-
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, true);
492-
self.ui.label(id!(image_viewer_status_label)).set_text(cx, image_viewer_error_to_string(error));
493-
}
494-
}
495-
self.ui.modal(id!(image_viewer)).open(cx);
496-
continue;
497-
}
498-
Some(ImageViewerAction::Hide) => {
499-
self.ui.modal(id!(image_viewer)).close(cx);
500-
continue;
501-
}
502-
_ => {}
454+
if self.handle_image_viewer_action(cx, action) {
455+
continue;
503456
}
504457

505458
// `VerificationAction`s come from a background thread, so they are NOT widget actions.
@@ -595,7 +548,29 @@ impl AppMain for App {
595548
}
596549
}
597550
}
551+
552+
// Ensure all draw events are handled on the main UI thread regardless of modal consuming the events.
553+
if let Event::Draw(_) = event {
554+
let scope = &mut Scope::with_data(&mut self.app_state);
555+
self.ui.handle_event(cx, event, scope);
556+
return;
557+
}
598558

559+
// If the image viewer modal is really opened, handles non-Draw events using the modal.
560+
let image_viewer_modal = self.ui.modal(id!(image_viewer));
561+
if image_viewer_modal.is_open() &&image_viewer_modal.area().rect(cx).size.y > 200.0 {
562+
let scope = &mut Scope::with_data(&mut self.app_state);
563+
self.ui.modal(id!(image_viewer)).handle_event(cx, event, scope);
564+
if let Event::Actions(actions) = event {
565+
for action in actions {
566+
if self.handle_image_viewer_action(cx, action) {
567+
continue
568+
}
569+
}
570+
}
571+
return;
572+
}
573+
599574
// Forward events to the MatchEvent trait implementation.
600575
self.match_event(cx, event);
601576
let scope = &mut Scope::with_data(&mut self.app_state);
@@ -704,6 +679,73 @@ impl App {
704679
closure(cx);
705680
}
706681
}
682+
683+
/// Handles actions for the image viewer.
684+
/// Returns a boolean, is true continues the actions for loop.
685+
fn handle_image_viewer_action(&mut self, cx: &mut Cx, action: &Box<dyn ActionTrait>) -> bool {
686+
match action.downcast_ref() {
687+
Some(ImageViewerAction::Show(load_state)) => {
688+
match load_state {
689+
LoadState::Loading(thumbnail_data) => {
690+
self.ui.modal(id!(image_viewer)).open(cx);
691+
self.ui.image_viewer(id!(image_viewer_inner)).reset(cx);
692+
self.ui.view(id!(image_viewer_loading_spinner_view)).set_visible(cx, true);
693+
self.ui.label(id!(image_viewer_status_label)).set_text(cx, "Loading...");
694+
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, false);
695+
self.ui.view(id!(footer)).apply_over(cx, live!{
696+
height: 50
697+
});
698+
let _ = self.ui.image_viewer(id!(image_viewer_inner)).display_rotated_image(cx, thumbnail_data);
699+
}
700+
LoadState::Loaded(image_bytes) => {
701+
self.ui.modal(id!(image_viewer)).open(cx);
702+
self.ui.view(id!(image_viewer_loading_spinner_view)).set_visible(cx, false);
703+
if let Err(error) = self.ui.image_viewer(id!(image_viewer_inner)).display_rotated_image(cx, image_bytes) {
704+
// Reset the image viewer to clear any previous image
705+
self.ui.image_viewer(id!(image_viewer_inner)).reset(cx);
706+
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, true);
707+
let err = match error {
708+
ImageError::JpgDecode(_) | ImageError::PngDecode(_) => ImageViewerError::UnsupportedFormat,
709+
ImageError::EmptyData => ImageViewerError::BadData,
710+
ImageError::PathNotFound(_) => ImageViewerError::NotFound,
711+
ImageError::UnsupportedFormat => ImageViewerError::UnsupportedFormat,
712+
_ => ImageViewerError::BadData,
713+
};
714+
self.ui.label(id!(image_viewer_status_label)).set_text(cx, image_viewer_error_to_string(&err));
715+
} else {
716+
self.ui.view(id!(zoom_button_view)).set_visible(cx, true);
717+
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, false);
718+
self.ui.label(id!(image_viewer_status_label)).set_text(cx, "");
719+
// Collapse the footer
720+
self.ui.view(id!(footer)).apply_over(cx, live!{
721+
height: 0
722+
});
723+
}
724+
}
725+
LoadState::Error(error) => {
726+
if self.ui.modal(id!(image_viewer)).is_open() {
727+
// Reset the image viewer to clear any previous image
728+
self.ui.image_viewer(id!(image_viewer_inner)).reset(cx);
729+
self.ui.view(id!(image_viewer_loading_spinner_view)).set_visible(cx, false);
730+
self.ui.view(id!(image_viewer_forbidden_view)).set_visible(cx, true);
731+
self.ui.label(id!(image_viewer_status_label)).set_text(cx, image_viewer_error_to_string(error));
732+
// Expand the footer
733+
self.ui.view(id!(footer)).apply_over(cx, live!{
734+
height: 50
735+
});
736+
}
737+
}
738+
}
739+
true
740+
}
741+
Some(ImageViewerAction::Hide) => {
742+
self.ui.modal(id!(image_viewer)).close(cx);
743+
self.ui.room_image_message_detail(id!(image_detail)).reset_state(cx);
744+
true
745+
}
746+
_ => false
747+
}
748+
}
707749
}
708750

709751
/// App-wide state that is stored persistently across multiple app runs

src/home/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod welcome_screen;
2121
pub mod event_reaction_list;
2222
pub mod new_message_context_menu;
2323
pub mod link_preview;
24+
pub mod room_image_message_detail;
2425

2526
pub fn live_design(cx: &mut Cx) {
2627
home_screen::live_design(cx);
@@ -44,4 +45,5 @@ pub fn live_design(cx: &mut Cx) {
4445
light_themed_dock::live_design(cx);
4546
event_reaction_list::live_design(cx);
4647
link_preview::live_design(cx);
48+
room_image_message_detail::live_design(cx);
4749
}

0 commit comments

Comments
 (0)