@@ -12,13 +12,13 @@ use std::sync::Arc;
1212
1313use icy_engine:: Screen ;
1414use icy_ui:: {
15- widget:: { container, stack } ,
16- Alignment , Element , Length , Task ,
15+ widget:: { container, scroll_area , scrollable } ,
16+ Element , Length , Task ,
1717} ;
1818
1919use icy_engine_gui:: theme:: main_area_background;
2020use icy_engine_gui:: TerminalMessage ;
21- use icy_engine_gui:: { EditorMarkers , HorizontalScrollbarOverlay , MonitorSettings , ScalingMode , ScrollbarOverlay , Terminal , TerminalView , ZoomMessage } ;
21+ use icy_engine_gui:: { EditorMarkers , MonitorSettings , ScalingMode , Terminal , TerminalView , ZoomMessage } ;
2222use parking_lot:: { Mutex , RwLock } ;
2323
2424/// Canvas view state for the ANSI editor
@@ -254,51 +254,59 @@ impl CanvasView {
254254 /// * `editor_markers` - Optional editor markers (layer bounds, selection, etc.)
255255 /// The caller should set layer_bounds, selection_rect, etc. before calling view().
256256 pub fn view ( & self , editor_markers : Option < EditorMarkers > ) -> Element < ' _ , TerminalMessage > {
257- // Use TerminalView to render with CRT shader effect
258- let terminal_view = TerminalView :: show_with_effects ( & self . terminal , Arc :: new ( self . monitor_settings . read ( ) . clone ( ) ) , editor_markers) ;
259-
260- // Get scrollbar info using shared logic from icy_engine_gui
261- let scrollbar_info = self . terminal . scrollbar_info ( ) ;
262-
263- if scrollbar_info. needs_any_scrollbar ( ) {
264- let mut layers: Vec < Element < ' _ , TerminalMessage > > = vec ! [ terminal_view] ;
265-
266- // Add vertical scrollbar if needed - uses viewport directly, no messages needed
267- if scrollbar_info. needs_vscrollbar {
268- let vscrollbar_view: Element < ' _ , ( ) > = ScrollbarOverlay :: new ( & self . terminal . viewport ) . view ( ) ;
269- // Map () to CanvasMessage - scrollbar mutates viewport directly via Arc<RwLock>
270- let vscrollbar_mapped: Element < ' _ , TerminalMessage > = vscrollbar_view. map ( |_| unreachable ! ( ) ) ;
271- let vscrollbar_container: container:: Container < ' _ , TerminalMessage > =
272- container ( vscrollbar_mapped) . width ( Length :: Fill ) . height ( Length :: Fill ) . align_x ( Alignment :: End ) ;
273- layers. push ( vscrollbar_container. into ( ) ) ;
274- }
275-
276- // Add horizontal scrollbar if needed - uses viewport directly, no messages needed
277- if scrollbar_info. needs_hscrollbar {
278- let hscrollbar_view: Element < ' _ , ( ) > = HorizontalScrollbarOverlay :: new ( & self . terminal . viewport ) . view ( ) ;
279- let hscrollbar_mapped: Element < ' _ , TerminalMessage > = hscrollbar_view. map ( |_| unreachable ! ( ) ) ;
280- let hscrollbar_container: container:: Container < ' _ , TerminalMessage > =
281- container ( hscrollbar_mapped) . width ( Length :: Fill ) . height ( Length :: Fill ) . align_y ( Alignment :: End ) ;
282- layers. push ( hscrollbar_container. into ( ) ) ;
283- }
257+ // Get scrollable content size from screen (virtual_size includes full document)
258+ let screen = self . terminal . screen . lock ( ) ;
259+ let virtual_size = screen. virtual_size ( ) ;
260+ drop ( screen) ;
261+
262+ // Get zoom from viewport
263+ let zoom = self . terminal . viewport . read ( ) . zoom ;
264+
265+ // Scrollable size in zoomed pixels (for scroll_area)
266+ let scrollable_width = virtual_size. width as f32 * zoom;
267+ let scrollable_height = virtual_size. height as f32 * zoom;
268+
269+ // Scrollable size in content pixels (for viewport)
270+ let scrollable_content_width = virtual_size. width as f32 ;
271+ let scrollable_content_height = virtual_size. height as f32 ;
272+
273+ let scrollable_size = icy_ui:: Size :: new ( scrollable_width, scrollable_height) ;
274+ let monitor_settings = Arc :: new ( self . monitor_settings . read ( ) . clone ( ) ) ;
275+ let viewport_arc = self . terminal . viewport . clone ( ) ;
276+
277+ let content = scroll_area ( )
278+ . width ( Length :: Fill )
279+ . height ( Length :: Fill )
280+ . direction ( scrollable:: Direction :: Both {
281+ vertical : scrollable:: Scrollbar :: new ( ) . width ( 8 ) . scroller_width ( 6 ) ,
282+ horizontal : scrollable:: Scrollbar :: new ( ) . width ( 8 ) . scroller_width ( 6 ) ,
283+ } )
284+ . show_viewport ( scrollable_size, move |scroll_viewport| {
285+ // Update terminal viewport with scroll position and content size from scroll_area
286+ // scroll_viewport.x/y are in zoomed pixel coordinates
287+ {
288+ let mut vp = viewport_arc. write ( ) ;
289+ // Convert from zoomed coordinates back to content coordinates
290+ vp. scroll_x = scroll_viewport. x / zoom;
291+ vp. scroll_y = scroll_viewport. y / zoom;
292+ vp. visible_width = scroll_viewport. width ;
293+ vp. visible_height = scroll_viewport. height ;
294+ // Set scrollable content size (in content pixels, not zoomed)
295+ vp. content_width = scrollable_content_width;
296+ vp. content_height = scrollable_content_height;
297+ }
298+
299+ TerminalView :: show_with_effects ( & self . terminal , monitor_settings. clone ( ) , editor_markers. clone ( ) )
300+ . map ( |msg| msg)
301+ } ) ;
284302
285- container ( stack ( layers) )
286- . width ( Length :: Fill )
287- . height ( Length :: Fill )
288- . style ( |theme : & icy_ui:: Theme | container:: Style {
289- background : Some ( icy_ui:: Background :: Color ( main_area_background ( theme) ) ) ,
290- ..Default :: default ( )
291- } )
292- . into ( )
293- } else {
294- container ( terminal_view)
295- . width ( Length :: Fill )
296- . height ( Length :: Fill )
297- . style ( |theme : & icy_ui:: Theme | container:: Style {
298- background : Some ( icy_ui:: Background :: Color ( main_area_background ( theme) ) ) ,
299- ..Default :: default ( )
300- } )
301- . into ( )
302- }
303+ container ( content)
304+ . width ( Length :: Fill )
305+ . height ( Length :: Fill )
306+ . style ( |theme : & icy_ui:: Theme | container:: Style {
307+ background : Some ( icy_ui:: Background :: Color ( main_area_background ( theme) ) ) ,
308+ ..Default :: default ( )
309+ } )
310+ . into ( )
303311 }
304312}
0 commit comments