@@ -10,7 +10,7 @@ use makepad_widgets::{image_cache::ImageError, makepad_micro_serde::*, *};
1010use matrix_sdk:: ruma:: { OwnedRoomId , RoomId } ;
1111use 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
0 commit comments