Skip to content

Commit cae7ca2

Browse files
committed
Replaced overlay scrollbars with scrollarea calls.
1 parent 50edfd5 commit cae7ca2

File tree

12 files changed

+345
-752
lines changed

12 files changed

+345
-752
lines changed

crates/icy_draw/src/ui/editor/ansi/dialog/tdf_font_selector.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use icy_engine_gui::{
1818
dialog_area, modal_container, primary_button, secondary_button, separator, ButtonType, Dialog, DialogAction, DIALOG_SPACING, DIALOG_WIDTH_XARGLE,
1919
TEXT_SIZE_NORMAL, TEXT_SIZE_SMALL,
2020
},
21-
wrap_with_scrollbars, Viewport,
21+
Viewport,
2222
};
2323
use icy_ui::{
2424
advanced::{
@@ -1046,9 +1046,8 @@ impl Dialog<Message> for TdfFontSelectorDialog {
10461046
// Font list canvas (virtualized)
10471047
let font_list: Element<'_, Message> = self.list_widget();
10481048

1049-
// Wrap canvas with scrollbar overlay
1050-
let needs_scrollbar = self.filtered_fonts.len() as f32 * FONT_ITEM_HEIGHT > LIST_HEIGHT;
1051-
let canvas_with_scrollbar = wrap_with_scrollbars(font_list, &self.viewport, needs_scrollbar, false);
1049+
// Font list canvas (scrollbars handled elsewhere if needed)
1050+
let canvas_with_scrollbar = font_list;
10521051

10531052
let list_container = container(canvas_with_scrollbar)
10541053
.width(Length::Fill)

crates/icy_draw/src/ui/editor/ansi/widget/canvas/mod.rs

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ use std::sync::Arc;
1212

1313
use icy_engine::Screen;
1414
use icy_ui::{
15-
widget::{container, stack},
16-
Alignment, Element, Length, Task,
15+
widget::{container, scroll_area, scrollable},
16+
Element, Length, Task,
1717
};
1818

1919
use icy_engine_gui::theme::main_area_background;
2020
use 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};
2222
use 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
}

crates/icy_draw/src/ui/editor/ansi/widget/layer_view/view.rs

Lines changed: 14 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ use icy_engine_edit::EditState;
3333
use icy_engine_gui::theme::main_area_background;
3434
use icy_engine_gui::CheckerboardColors;
3535
use icy_engine_gui::DoubleClickDetector;
36-
use icy_engine_gui::{wrap_with_scrollbars, Viewport};
37-
use icy_ui::widget::menu::{context_menu, items as menu_items_fn, Item as MenuItemDef, Tree as MenuTree};
36+
use icy_engine_gui::Viewport;
37+
use icy_ui::menu::{item as menu_item, MenuNode};
38+
use icy_ui::widget::menu::context_menu;
3839
use parking_lot::Mutex;
3940

4041
use crate::fl;
@@ -66,32 +67,6 @@ const PREVIEW_ATLAS_H: u32 = PREVIEW_TEX_H * PREVIEW_ATLAS_ROWS;
6667

6768
const MAX_LABEL_CHARS: usize = 64;
6869

69-
/// Context menu action for layers
70-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71-
pub enum LayerContextAction {
72-
Properties(usize),
73-
NewLayer,
74-
Duplicate(usize),
75-
MergeDown(usize),
76-
Delete(usize),
77-
Clear(usize),
78-
}
79-
80-
impl icy_ui::widget::menu::Action for LayerContextAction {
81-
type Message = LayerMessage;
82-
83-
fn message(&self) -> Self::Message {
84-
match *self {
85-
LayerContextAction::Properties(i) => LayerMessage::EditLayer(i),
86-
LayerContextAction::NewLayer => LayerMessage::Add,
87-
LayerContextAction::Duplicate(i) => LayerMessage::Duplicate(i),
88-
LayerContextAction::MergeDown(i) => LayerMessage::MergeDown(i),
89-
LayerContextAction::Delete(i) => LayerMessage::Remove(i),
90-
LayerContextAction::Clear(i) => LayerMessage::Clear(i),
91-
}
92-
}
93-
}
94-
9570
/// Messages for the layer view
9671
#[derive(Clone, Debug)]
9772
pub enum LayerMessage {
@@ -577,31 +552,26 @@ impl LayerView {
577552
}
578553

579554
/// Build the context menu items for a layer
580-
fn build_context_menu_items(index: usize, layer_count: usize) -> Vec<MenuTree<'static, LayerMessage, Theme, icy_ui::Renderer>> {
581-
use icy_ui::widget::menu::KeyBind;
582-
use std::collections::HashMap;
583-
584-
let key_binds: HashMap<KeyBind, LayerContextAction> = HashMap::new();
585-
555+
fn build_context_menu_items(index: usize, layer_count: usize) -> Vec<MenuNode<LayerMessage>> {
586556
let mut items = vec![
587-
MenuItemDef::Button(fl!("layer_tool_menu_layer_properties"), LayerContextAction::Properties(index)),
588-
MenuItemDef::Button(fl!("layer_tool_menu_new_layer"), LayerContextAction::NewLayer),
589-
MenuItemDef::Button(fl!("layer_tool_menu_duplicate_layer"), LayerContextAction::Duplicate(index)),
557+
menu_item!(fl!("layer_tool_menu_layer_properties"), LayerMessage::EditLayer(index)),
558+
menu_item!(fl!("layer_tool_menu_new_layer"), LayerMessage::Add),
559+
menu_item!(fl!("layer_tool_menu_duplicate_layer"), LayerMessage::Duplicate(index)),
590560
];
591561

592562
// Merge is only available if not the bottom layer
593563
if index > 0 {
594-
items.push(MenuItemDef::Button(fl!("layer_tool_menu_merge_layer"), LayerContextAction::MergeDown(index)));
564+
items.push(menu_item!(fl!("layer_tool_menu_merge_layer"), LayerMessage::MergeDown(index)));
595565
}
596566

597567
// Delete is only available if there's more than one layer
598568
if layer_count > 1 {
599-
items.push(MenuItemDef::Button(fl!("layer_tool_menu_delete_layer"), LayerContextAction::Delete(index)));
569+
items.push(menu_item!(fl!("layer_tool_menu_delete_layer"), LayerMessage::Remove(index)));
600570
}
601571

602-
items.push(MenuItemDef::Button(fl!("layer_tool_menu_clear_layer"), LayerContextAction::Clear(index)));
572+
items.push(menu_item!(fl!("layer_tool_menu_clear_layer"), LayerMessage::Clear(index)));
603573

604-
menu_items_fn(&key_binds, items)
574+
items
605575
}
606576

607577
/// Create a single menu item button
@@ -688,8 +658,8 @@ impl LayerView {
688658
}
689659
.into();
690660

691-
let needs_scrollbar = self.viewport.borrow().is_scrollable_y();
692-
let list_with_scrollbar = wrap_with_scrollbars(list_widget, &self.viewport, needs_scrollbar, false);
661+
// Layer list (scrollbars handled elsewhere if needed)
662+
let list_with_scrollbar = list_widget;
693663

694664
let scroll_y = self.viewport.borrow().scroll_y;
695665
let icon_color = theme.background.on;
@@ -763,7 +733,7 @@ impl LayerView {
763733
// Wrap with context menu (for current selection)
764734
if layer_count > 0 {
765735
let menu_items = Self::build_context_menu_items(current_layer, layer_count);
766-
context_menu(content, Some(menu_items)).into()
736+
context_menu(content, &menu_items).into()
767737
} else {
768738
content.into()
769739
}

crates/icy_engine_gui/src/scrollbar/mod.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,27 @@
55
pub mod state;
66
pub use state::*;
77

8-
pub mod overlay;
9-
pub use overlay::{ScrollbarOverlay, ScrollbarOverlayCallback, ScrollbarOverlayState, ViewportAccess};
8+
// Overlay scrollbars are deprecated - use scroll_area with show_viewport instead
9+
// Keeping source files for reference/helper methods
10+
// pub mod overlay;
11+
// pub use overlay::{ScrollbarOverlay, ScrollbarOverlayCallback, ScrollbarOverlayState, ViewportAccess};
1012

11-
pub mod horizontal_overlay;
12-
pub use horizontal_overlay::{HorizontalScrollbarOverlay, HorizontalScrollbarOverlayCallback, HorizontalScrollbarOverlayState};
13+
// pub mod horizontal_overlay;
14+
// pub use horizontal_overlay::{HorizontalScrollbarOverlay, HorizontalScrollbarOverlayCallback, HorizontalScrollbarOverlayState};
1315

14-
pub mod info;
15-
pub use info::*;
16+
// pub mod info;
17+
// pub use info::*;
18+
19+
use icy_ui::Element;
20+
21+
/// Deprecated: Wrap content with scrollbar overlays
22+
/// This is now a no-op - use scroll_area with show_viewport instead for native scrollbars
23+
#[deprecated(note = "Use scroll_area with show_viewport instead")]
24+
pub fn wrap_with_scrollbars<'a, Message: 'a>(
25+
content: Element<'a, Message>,
26+
_needs_vscrollbar: bool,
27+
_needs_hscrollbar: bool,
28+
) -> Element<'a, Message> {
29+
// Just return content - scrollbars should be handled by scroll_area now
30+
content
31+
}

crates/icy_engine_gui/src/terminal/crt_program.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ impl<'a> CRTShaderProgram<'a> {
280280
scroll_offset_y = params.scroll_offset_y;
281281
scroll_offset_x = params.scroll_offset_x;
282282
}
283-
284283
if cfg!(debug_assertions) && std::env::var("ICY_DEBUG_VIEWPORT").is_ok() && viewport_changed {
285284
let max_scroll_x = (content_width - visible_width).max(0.0);
286285
let max_scroll_y = (content_height - visible_height).max(0.0);

crates/icy_engine_gui/src/terminal/crt_state.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,27 @@ pub fn create_crt_shader<'a>(
322322
monitor_settings: Arc<MonitorSettings>,
323323
editor_markers: Option<crate::EditorMarkers>,
324324
) -> Element<'a, TerminalMessage> {
325-
// Let the parent wrapper decide sizing; shader can just be Fill.
326-
shader(CRTShaderProgram::new(term, monitor_settings, editor_markers))
327-
.width(icy_ui::Length::Fill)
328-
.height(icy_ui::Length::Fill)
329-
.into()
325+
// Get content size directly from screen (virtual_size is in pixels)
326+
let screen = term.screen.lock();
327+
let virtual_size = screen.virtual_size();
328+
drop(screen);
329+
330+
// Get zoom from viewport (still needed for zoom level)
331+
let zoom = term.viewport.read().zoom;
332+
333+
let zoomed_width = (virtual_size.width as f32 * zoom).max(1.0);
334+
let zoomed_height = (virtual_size.height as f32 * zoom).max(1.0);
335+
336+
// Wrap the shader in a container with fixed size so it works inside scrollable
337+
// The shader itself still uses Fill to expand into this container
338+
icy_ui::widget::container(
339+
shader(CRTShaderProgram::new(term, monitor_settings, editor_markers))
340+
.width(icy_ui::Length::Fill)
341+
.height(icy_ui::Length::Fill),
342+
)
343+
.width(icy_ui::Length::Fixed(zoomed_width))
344+
.height(icy_ui::Length::Fixed(zoomed_height))
345+
.into()
330346
}
331347

332348
static SCALE_FACTOR_BITS: AtomicU32 = AtomicU32::new(f32::to_bits(1.0));

crates/icy_engine_gui/src/terminal/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl Terminal {
103103

104104
/// Update viewport when screen size changes
105105
pub fn update_viewport_size(&mut self) {
106-
let scr = self.screen.lock();
106+
let scr: parking_lot::lock_api::MutexGuard<'_, parking_lot::RawMutex, Box<dyn Screen + 'static>> = self.screen.lock();
107107
let virtual_size = scr.virtual_size();
108108
drop(scr);
109109

0 commit comments

Comments
 (0)