diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 85412cb53d..d8b415f922 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -70,7 +70,7 @@ pub use selection_marks::SelectionMarks; #[cfg(test)] pub(crate) use task_bar::chip_paints; use task_bar::task_bar; -pub(crate) use task_bar::{has_colors, task_bar_rect, task_bar_shows_type}; +pub(crate) use task_bar::{has_colors, task_bar_rect, task_bar_shows_fonts, task_bar_shows_type}; #[cfg(test)] use tool_overlays::{DIAL_RADIUS, HiddenMarks}; pub use tool_overlays::{HiddenChars, SpellingMarks}; diff --git a/crates/ui-egui/src/canvas/task_bar.rs b/crates/ui-egui/src/canvas/task_bar.rs index c0422e0347..e908640c25 100644 --- a/crates/ui-egui/src/canvas/task_bar.rs +++ b/crates/ui-egui/src/canvas/task_bar.rs @@ -13,6 +13,12 @@ pub(crate) fn task_bar_rect(ctx: &egui::Context) -> Option { ctx.memory(|m| m.areas().is_visible(&egui::LayerId::new(egui::Order::Middle, id)).then(|| m.area_rect(id)).flatten()) } +/// Whether the Contextual Task Bar, where it was last drawn, held the font controls: on a canvas +/// too narrow for them it leaves them out. +pub(crate) fn task_bar_shows_fonts(ctx: &egui::Context) -> bool { + task_bar_rect(ctx).is_some() && ctx.data(|d| d.get_temp::(task_bar_id().with("fonts")).unwrap_or(false)) +} + /// Whether the objects `ids` (or anything inside them) are painted with a colour: a solid colour, /// a gradient's stops or a mesh's points, which Recolor Artwork can change. pub(crate) fn has_colors(doc: &vectorcraft_doc::Document, ids: &[vectorcraft_doc::NodeId]) -> bool { @@ -235,6 +241,7 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { let bounds = xf.rect.shrink(8.0); // On a canvas too narrow for the type controls at their narrowest, the bar leaves them out. let fonts = fonts && bounds.width() - 18.0 >= crate::panels::character::task_bar_min_room(); + ui.ctx().data_mut(|d| d.insert_temp(task_bar_id().with("fonts"), fonts)); // The width of the bar's frame (margins and stroke), its handle, Fill and Stroke chips, // Recolor Artwork, Lock or Shape Properties, and More Options, each after a 6-point gap. let chips: f32 = diff --git a/crates/ui-egui/src/font_menu.rs b/crates/ui-egui/src/font_menu.rs index 01e4d9e524..0bf87a88cd 100644 --- a/crates/ui-egui/src/font_menu.rs +++ b/crates/ui-egui/src/font_menu.rs @@ -70,7 +70,12 @@ impl WheelOwner { Self::ControlPopover(anchor) => { return app.ui.control_bar && app.ui.screen_mode < 2 && widgets::popover_is_open(ctx, anchor); } - Self::TaskBar => return crate::canvas::task_bar_shows_type(app) && crate::canvas::task_bar_rect(ctx).is_some(), + Self::TaskBar => { + return app.ui.task_bar + && app.ui.screen_mode < 3 + && crate::canvas::task_bar_shows_type(app) + && crate::canvas::task_bar_shows_fonts(ctx); + } Self::Character => "character", Self::Properties => { let selected = app.session.active().and_then(|document| document.selection.objects.first().and_then(|id| document.doc.node(*id))); diff --git a/crates/ui-egui/src/tests/taskbar.rs b/crates/ui-egui/src/tests/taskbar.rs index 202d6eb4c1..58f42b57a2 100644 --- a/crates/ui-egui/src/tests/taskbar.rs +++ b/crates/ui-egui/src/tests/taskbar.rs @@ -769,6 +769,47 @@ fn selected_types_font_controls_take_a_second_row_on_a_narrow_canvas() { assert!(canvas.contains_rect(r) && r.height() < 60.0, "no font controls on a 160 px screen: {r:?} in {canvas:?}"); } +/// When the canvas narrows so far that the bar leaves its font controls out, the font family +/// field it drew before no longer takes the mouse wheel. +#[test] +fn a_task_bar_without_its_font_controls_leaves_their_wheel() { + let mut app = app(); + app.run("tool.select", json!({"tool": "type"})).unwrap(); + app.run("text.create", json!({"x": 20, "y": 60, "text": "Hello", "size": 20})).unwrap(); + app.session.apply_actions(vec![vectorcraft_tools::Action::Notify("text.editNew".into())]).unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let draw = |app: &mut VectorcraftApp, width: f32| { + let screen = Rect::from_min_size(Pos2::ZERO, vec2(width, 700.0)); + for _ in 0..3 { + let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| canvas::show(app, ui)); + out.textures_delta.clear(); + } + }; + draw(&mut app, 1000.0); + let bar = canvas::task_bar_rect(&ctx).expect("the task bar shows"); + let viewport = egui::ViewportId::ROOT; + let points = (0..(bar.width() / 4.0) as usize).flat_map(|x| (0..(bar.height() / 4.0) as usize).map(move |y| (x, y))); + let field = points + .map(|(x, y)| bar.min + vec2(x as f32 * 4.0, y as f32 * 4.0)) + .find(|p| crate::font_menu::wheel_field_layer_for_test(&ctx, viewport, *p).is_some()) + .expect("the bar's font family field takes the wheel"); + draw(&mut app, 160.0); + assert!(canvas::task_bar_rect(&ctx).is_some(), "the bar still shows, without its font controls"); + crate::font_menu::sync_wheel_target(&app, &ctx, viewport); + assert!(crate::font_menu::wheel_field_layer_for_test(&ctx, viewport, field).is_none(), "the left-out field at {field:?} still takes the wheel"); + + // Hiding the bar (Window › Contextual Task Bar) releases the field before the next layout. + draw(&mut app, 1000.0); + assert!(crate::font_menu::wheel_field_layer_for_test(&ctx, viewport, field).is_some(), "the field is back"); + app.run("window.taskBar", json!({})).unwrap(); + crate::font_menu::sync_wheel_target(&app, &ctx, viewport); + assert!( + crate::font_menu::wheel_field_layer_for_test(&ctx, viewport, field).is_none(), + "the hidden bar's field at {field:?} still takes the wheel" + ); +} + /// While a Type tool edits type, the Fill chip shows the selected characters' fill, and "?" where /// their fills differ. #[test]