diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 9f15ca0f31..80d7aea492 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -3460,7 +3460,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { let show_secured_notice = app.show_secured_notice; // Asking means opening the system clipboard (and decoding any image on it), so it's asked // once when a right-click opens a menu, not on every frame. - let can_paste_key = egui::Id::new("can-paste-image"); + let can_paste_key = egui::Id::unique("can-paste-image"); let can_paste_image = if ui.input(|i| i.pointer.secondary_pressed()) { let can = app.can_paste_image(); ui.data_mut(|d| d.insert_temp(can_paste_key, can)); @@ -5539,29 +5539,32 @@ fn selection_actions( let x = ideal_x.clamp(viewport.left() + 8.0, max_x); let y = if rect.bottom() + 48.0 < viewport.bottom() { rect.bottom() + 6.0 } else { (rect.top() - 44.0).max(viewport.top() + 6.0) }; let mut chosen = None; - egui::Area::new(egui::Id::new(("text-selection-actions", view.id.0))).order(egui::Order::Foreground).fixed_pos(pos2(x, y)).show(ui.ctx(), |ui| { - egui::Frame::popup(ui.style()).corner_radius(CornerRadius::same(12)).show(ui, |ui| { - ui.spacing_mut().item_spacing.x = 2.0; - ui.horizontal(|ui| { - ui.add_enabled_ui(can_comment, |ui| { - for (icon, tooltip, action) in [ - ("message-square-plus", tl!("Add comment"), SelectionAction::Note), - ("highlighter", tl!("Highlight"), SelectionAction::Markup(comments::CommentTool::Highlight)), - ("underline", tl!("Underline"), SelectionAction::Markup(comments::CommentTool::Underline)), - ("strikethrough", tl!("Strikethrough"), SelectionAction::Markup(comments::CommentTool::StrikeOut)), - ("replace", tl!("Replace text"), SelectionAction::Replace), - ] { - if icons::button(ui, icon, 26.0, false, tooltip).clicked() { - chosen = Some(action); + egui::Area::new(egui::Id::unique(("text-selection-actions", view.id.0))).order(egui::Order::Foreground).fixed_pos(pos2(x, y)).show( + ui.ctx(), + |ui| { + egui::Frame::popup(ui.style()).corner_radius(CornerRadius::same(12)).show(ui, |ui| { + ui.spacing_mut().item_spacing.x = 2.0; + ui.horizontal(|ui| { + ui.add_enabled_ui(can_comment, |ui| { + for (icon, tooltip, action) in [ + ("message-square-plus", tl!("Add comment"), SelectionAction::Note), + ("highlighter", tl!("Highlight"), SelectionAction::Markup(comments::CommentTool::Highlight)), + ("underline", tl!("Underline"), SelectionAction::Markup(comments::CommentTool::Underline)), + ("strikethrough", tl!("Strikethrough"), SelectionAction::Markup(comments::CommentTool::StrikeOut)), + ("replace", tl!("Replace text"), SelectionAction::Replace), + ] { + if icons::button(ui, icon, 26.0, false, tooltip).clicked() { + chosen = Some(action); + } } + }); + if ui.add_enabled_ui(can_copy, |ui| icons::button(ui, "copy", 26.0, false, tl!("Copy"))).inner.clicked() { + chosen = Some(SelectionAction::Copy); } }); - if ui.add_enabled_ui(can_copy, |ui| icons::button(ui, "copy", 26.0, false, tl!("Copy"))).inner.clicked() { - chosen = Some(SelectionAction::Copy); - } }); - }); - }); + }, + ); let Some(action) = chosen else { return false }; if let SelectionAction::Copy = action { // Copying closes the bar but keeps the words selected, for ⌘C or a second action. diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 318450cd19..397ee50bce 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -1379,7 +1379,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) { .hint_text(tl!("Search tools…")) .desired_width(ui.available_width() - 32.0), ); - search_resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::TextEdit, ui.is_enabled(), tl!("Search tools…"))); + search_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::TextInput, ui.is_enabled(), tl!("Search tools…"))); if !app.pin_tool_search.is_empty() && widgets::ghost_button(ui, "x", tl!("Clear search")).clicked() { app.pin_tool_search.clear(); } @@ -1408,7 +1408,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) { let pinned = app.is_tool_pinned(g.id); let (rect, resp) = ui.allocate_exact_size(egui::vec2(ui.available_width(), 44.0), egui::Sense::click()); - resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, label)); + resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, label)); let press = theme::Press::track(ui, &resp); press.wash(ui, rect, 6, false); let body = rect.translate(press.offset()); @@ -1425,7 +1425,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) { let btn_w = 78.0; let btn_rect = egui::Rect::from_center_size(body.right_center() - egui::vec2(btn_w / 2.0 + 8.0, 0.0), egui::vec2(btn_w, 26.0)); - let btn_resp = ui.interact(btn_rect, ui.id().with(("pin_modal_btn", g.id)), egui::Sense::click()); + let btn_resp = ui.interact(btn_rect, ui.scope_id().with(("pin_modal_btn", g.id)), egui::Sense::click()); let btn_hovered = btn_resp.hovered(); let (btn_fill, btn_stroke, text_color, btn_label) = if pinned { @@ -1436,7 +1436,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) { (egui::Color32::TRANSPARENT, egui::Stroke::new(1.0, t.border), t.text_muted, tl!("Pin")) }; let tip = if pinned { tl!("Unpin from Home") } else { tl!("Pin to Home") }; - btn_resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Button, true, pinned, format!("{tip}: {label}"))); + btn_resp.widget_info(|| egui::WidgetInfo::selected(egui::Role::Button, true, pinned, format!("{tip}: {label}"))); ui.painter().rect(btn_rect, egui::CornerRadius::same(6), btn_fill, btn_stroke, egui::StrokeKind::Inside); ui.painter().text(btn_rect.center(), egui::Align2::CENTER_CENTER, btn_label, theme::medium(12.0), text_color); diff --git a/crates/ui-egui/src/edit_text_ui.rs b/crates/ui-egui/src/edit_text_ui.rs index 8ff5e3267d..42339f4ecc 100644 --- a/crates/ui-egui/src/edit_text_ui.rs +++ b/crates/ui-egui/src/edit_text_ui.rs @@ -1227,9 +1227,9 @@ fn crop_input( let cancel = Rect::from_min_size(Pos2::new(done.min.x - spacing - cancel_w, y), egui::vec2(cancel_w, 22.0)); let reset = reset_w.map(|w| Rect::from_min_size(Pos2::new(cancel.min.x - spacing - w, y), egui::vec2(w, 22.0))); - let done_id = ui.id().with("crop_pill_done").with(page).with(i); - let cancel_id = ui.id().with("crop_pill_cancel").with(page).with(i); - let reset_id = ui.id().with("crop_pill_reset").with(page).with(i); + let done_id = ui.scope_id().with("crop_pill_done").with(page).with(i); + let cancel_id = ui.scope_id().with("crop_pill_cancel").with(page).with(i); + let reset_id = ui.scope_id().with("crop_pill_reset").with(page).with(i); let done_resp = ui.interact(done, done_id, egui::Sense::click()); let cancel_resp = ui.interact(cancel, cancel_id, egui::Sense::click()); @@ -1510,7 +1510,7 @@ fn user_box(xf: &PageXform, info: &DocInfo, page: usize, r: Rect) -> [f64; 4] { } pub(crate) fn image_menu_id() -> egui::Id { - egui::Id::new("image-menu") + egui::Id::unique("image-menu") } /// Images on a page: select, move, resize, right-click. Returns `true` when the pointer was used. @@ -1656,14 +1656,14 @@ pub(crate) fn image_input( } let mut handle_drag = None; - for (h, h_pos, handle_id) in obb.handles_with_ids(ui.id().with("img_handle")) { + for (h, h_pos, handle_id) in obb.handles_with_ids(ui.scope_id().with("img_handle")) { let handle_rect = Rect::from_center_size(h_pos, egui::vec2(18.0, 18.0)); let handle_resp = ui.interact(handle_rect, handle_id, egui::Sense::drag()); if allow_interaction && handle_resp.drag_started() { handle_drag = Some(h); } } - let rot_id = ui.id().with("img_handle").with(8); + let rot_id = ui.scope_id().with("img_handle").with(8); let rot_rect = Rect::from_center_size(if obb.is_rotated { obb.s_rot } else { knob }, egui::vec2(12.0, 12.0)); let rot_resp = ui.interact(rot_rect, rot_id, egui::Sense::drag()); let rot_drag = allow_interaction && rot_resp.drag_started(); diff --git a/crates/ui-egui/src/format_text_ui.rs b/crates/ui-egui/src/format_text_ui.rs index e7f6f8ea08..865c4f133f 100644 --- a/crates/ui-egui/src/format_text_ui.rs +++ b/crates/ui-egui/src/format_text_ui.rs @@ -113,7 +113,7 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, look: &mut AddedText, extras: ui.label(tl!("No openly licensed document fonts found. Load a font file.")); } Some(list) => { - let search_id = ui.id().with("installed-font-search"); + let search_id = ui.scope_id().with("installed-font-search"); let mut query = ui.data(|d| d.get_temp::(search_id)).unwrap_or_default(); ui.horizontal(|ui| { ui.label(egui::RichText::new("🔍").size(12.0)); @@ -177,7 +177,7 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, look: &mut AddedText, extras: } }) .response; - font_resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::ComboBox, ui.is_enabled(), tl!("Font"))); + font_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::ComboBox, ui.is_enabled(), tl!("Font"))); font_resp.on_hover_text(format!("{}\n{}", tl!("Font"), tl!("Thai and Arabic use regular fallback fonts when available."))); ui.add_space(4.0); @@ -193,7 +193,7 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, look: &mut AddedText, extras: } }) .response; - size_resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::ComboBox, ui.is_enabled(), tl!("Font size"))); + size_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::ComboBox, ui.is_enabled(), tl!("Font size"))); size_resp.on_hover_text(tl!("Font size")); let swatch = colour_button(ui, t, look.color); egui::Popup::menu(&swatch).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| { @@ -273,7 +273,7 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, look: &mut AddedText, extras: } }) .response; - spacing_resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::ComboBox, ui.is_enabled(), tl!("Line spacing"))); + spacing_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::ComboBox, ui.is_enabled(), tl!("Line spacing"))); ui.add_space(8.0); ui.add_enabled_ui(false, |ui| { labelled_icon(ui, t, "pilcrow", tl!("Paragraph spacing")); @@ -364,7 +364,7 @@ fn number_with_presets(ui: &mut egui::Ui, n: Number<'_>, presets: &[f64], width: let field = egui::DragValue::new(&mut *n.value).range(n.range).speed(n.speed).max_decimals(2).suffix(n.suffix); let height = ui.spacing().interact_size.y; let drag_resp = ui.add_sized([(width - PRESET_LIST - 2.0).max(30.0), height], field).labelled_by(label.0); - drag_resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::DragValue, ui.is_enabled(), label.1)); + drag_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::SpinButton, ui.is_enabled(), label.1)); let list = icons::button(ui, "chevron-down", PRESET_LIST, false, label.1); egui::Popup::menu(&list).show(|ui| { for &v in presets { diff --git a/crates/ui-egui/src/home.rs b/crates/ui-egui/src/home.rs index 781da2a35f..f46df87c80 100644 --- a/crates/ui-egui/src/home.rs +++ b/crates/ui-egui/src/home.rs @@ -34,7 +34,7 @@ fn tool_card(app: &mut PdfCraftApp, ui: &mut egui::Ui, t: &Tokens, g: &'static c let mut unpin_clicked = false; if allow_unpin && (ui.rect_contains_pointer(rect) || resp.hovered()) { let unpin_rect = Rect::from_center_size(body.right_top() + vec2(-18.0, 18.0), vec2(22.0, 22.0)); - let unpin_resp = ui.interact(unpin_rect, ui.id().with(("unpin_card_btn", g.id)), Sense::click()); + let unpin_resp = ui.interact(unpin_rect, ui.scope_id().with(("unpin_card_btn", g.id)), Sense::click()); unpin_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, tl!("Unpin tool"))); if unpin_resp.hovered() { ui.painter().rect_filled(unpin_rect, CornerRadius::same(4), t.pressed); diff --git a/crates/ui-egui/src/panels.rs b/crates/ui-egui/src/panels.rs index 73bac867e5..ef3be8e414 100644 --- a/crates/ui-egui/src/panels.rs +++ b/crates/ui-egui/src/panels.rs @@ -122,7 +122,7 @@ fn tool_row(ui: &mut egui::Ui, t: &Tokens, g: &ToolGroup, is_pinned: bool) -> (e icons::paint(ui, Rect::from_min_size(body.min + vec2(6.0, 7.0), vec2(20.0, 20.0)), g.icon, 19.0, hue(g)); let pin_rect = Rect::from_center_size(body.right_center() - vec2(14.0, 0.0), vec2(22.0, 22.0)); - let pin_resp = ui.interact(pin_rect, ui.id().with(("pin_toggle_row", g.id)), Sense::click()); + let pin_resp = ui.interact(pin_rect, ui.scope_id().with(("pin_toggle_row", g.id)), Sense::click()); let tip = if is_pinned { tl!("Unpin from Home") } else { tl!("Pin to Home") }; pin_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, tip)); let pin_hovered = pin_resp.hovered(); @@ -176,7 +176,7 @@ fn tool_row(ui: &mut egui::Ui, t: &Tokens, g: &ToolGroup, is_pinned: bool) -> (e }; icons::paint(ui, pin_rect.shrink(3.0), "pin", 15.0, pin_tint); let tip = if is_pinned { tl!("Unpin from Home") } else { tl!("Pin to Home") }; - pin_resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, true, tip)); + pin_resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, tip)); if theme::hand(pin_resp).on_hover_text(tip).clicked() { pin_clicked = true; } diff --git a/crates/ui-egui/src/updates.rs b/crates/ui-egui/src/updates.rs index 85f4337407..fa59f9a89f 100644 --- a/crates/ui-egui/src/updates.rs +++ b/crates/ui-egui/src/updates.rs @@ -14,7 +14,6 @@ pub const RELEASES_PAGE: &str = "https://github.com/storytold/pdfcraft/releases" #[cfg(not(target_arch = "wasm32"))] pub type UpdateSource = CheckSource; -#[derive(Default)] pub(crate) struct Updates { #[cfg(not(target_arch = "wasm32"))] updater: Updater, diff --git a/crates/ui-egui/tests/it/editing.rs b/crates/ui-egui/tests/it/editing.rs index d22188c6eb..a6820b3e77 100644 --- a/crates/ui-egui/tests/it/editing.rs +++ b/crates/ui-egui/tests/it/editing.rs @@ -7863,7 +7863,7 @@ fn clicking_after_image_context_menu_action_resets_menu_state_and_updates_select h.get_by_label("Flip horizontal").click(); h.run_steps(3); - let flag_persisted = h.ctx.data_mut(|d| d.get_persisted::(egui::Id::new("image-menu"))); + let flag_persisted = h.ctx.data_mut(|d| d.get_persisted::(egui::Id::unique("image-menu"))); assert_eq!(flag_persisted, Some(false), "image-menu flag should be reset to false"); // 7. Click image 1: should switch selection to image 1