refactor(editor): migrate icon picker to shared select
This commit is contained in:
parent
f6628e2b28
commit
f142bb90d4
|
|
@ -896,8 +896,8 @@ pub struct EditorUiState {
|
|||
/// Last-selected shape tool — drives the toolbar shape slot's
|
||||
/// icon. Always one of Rect / Ellipse / Polygon / Line / Pen.
|
||||
pub shape_tool: Tool,
|
||||
/// Whether the Toolbar's Icon action picker is open.
|
||||
pub icon_picker_open: bool,
|
||||
/// Toolbar/property icon picker interaction state.
|
||||
pub icon_picker: jian_widgets::components::select::SelectState,
|
||||
/// True when the icon picker should replace the selected icon
|
||||
/// instead of inserting a new icon at the canvas centre.
|
||||
pub icon_picker_replace_selection: bool,
|
||||
|
|
@ -909,9 +909,6 @@ pub struct EditorUiState {
|
|||
/// True after Cmd/Ctrl+A in the icon search box. The next edit
|
||||
/// replaces the whole search query.
|
||||
pub icon_picker_select_all: bool,
|
||||
/// Which icon-picker target the cursor is over (close / row /
|
||||
/// load-more) — drives the `theme.button_hover` wash.
|
||||
pub icon_picker_hover: Option<crate::icon_picker_button_state::IconPickerButton>,
|
||||
/// Remote Iconify search results appended by the desktop host.
|
||||
pub icon_picker_remote: crate::icon_picker_state::IconPickerRemoteState,
|
||||
/// Queued "load more" request drained asynchronously by desktop.
|
||||
|
|
@ -1227,12 +1224,11 @@ impl Default for EditorUiState {
|
|||
shape_picker: jian_widgets::components::select::SelectState::default(),
|
||||
toolbar_hover: None,
|
||||
shape_tool: Tool::Rect,
|
||||
icon_picker_open: false,
|
||||
icon_picker: jian_widgets::components::select::SelectState::default(),
|
||||
icon_picker_replace_selection: false,
|
||||
icon_picker_panel_pos: None,
|
||||
icon_picker_search: String::new(),
|
||||
icon_picker_select_all: false,
|
||||
icon_picker_hover: None,
|
||||
icon_picker_remote: crate::icon_picker_state::IconPickerRemoteState::default(),
|
||||
icon_picker_load_more_request: None,
|
||||
chat_model_picker: jian_widgets::components::select::SelectState::default(),
|
||||
|
|
@ -1352,6 +1348,30 @@ impl EditorUiState {
|
|||
changed
|
||||
}
|
||||
|
||||
pub fn open_icon_picker(&mut self, replace_selection: bool) {
|
||||
self.close_icon_picker();
|
||||
self.icon_picker.open = true;
|
||||
self.icon_picker_replace_selection = replace_selection;
|
||||
}
|
||||
|
||||
pub fn close_icon_picker(&mut self) -> bool {
|
||||
let changed = self.icon_picker.open
|
||||
|| self.icon_picker.hover.is_some()
|
||||
|| self.icon_picker.pressed.is_some()
|
||||
|| self.icon_picker.scroll.offset != 0.0
|
||||
|| self.icon_picker_replace_selection
|
||||
|| !self.icon_picker_search.is_empty()
|
||||
|| self.icon_picker_select_all;
|
||||
self.icon_picker.open = false;
|
||||
self.icon_picker.hover = None;
|
||||
self.icon_picker.pressed = None;
|
||||
self.icon_picker.scroll.offset = 0.0;
|
||||
self.icon_picker_replace_selection = false;
|
||||
self.icon_picker_search.clear();
|
||||
self.icon_picker_select_all = false;
|
||||
changed
|
||||
}
|
||||
|
||||
pub fn toggle_font_picker(&mut self) {
|
||||
let opening = !self.font_picker.open;
|
||||
self.close_font_picker();
|
||||
|
|
@ -1703,4 +1723,35 @@ mod tests {
|
|||
assert_eq!(ui.chat_model_picker.pressed, None);
|
||||
assert_eq!(ui.chat_model_picker.scroll.offset, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn icon_picker_helpers_reset_select_interaction_state_and_search() {
|
||||
let mut ui = EditorUiState::new();
|
||||
ui.icon_picker_replace_selection = true;
|
||||
ui.icon_picker_search = "home".to_string();
|
||||
ui.icon_picker_select_all = true;
|
||||
ui.icon_picker.hover = Some(1);
|
||||
ui.icon_picker.pressed = Some(1);
|
||||
|
||||
ui.open_icon_picker(false);
|
||||
assert!(ui.icon_picker.open);
|
||||
assert!(!ui.icon_picker_replace_selection);
|
||||
assert!(ui.icon_picker_search.is_empty());
|
||||
assert!(!ui.icon_picker_select_all);
|
||||
assert_eq!(ui.icon_picker.hover, None);
|
||||
assert_eq!(ui.icon_picker.pressed, None);
|
||||
|
||||
ui.icon_picker_replace_selection = true;
|
||||
ui.icon_picker_search = "settings".to_string();
|
||||
ui.icon_picker_select_all = true;
|
||||
ui.icon_picker.hover = Some(0);
|
||||
ui.icon_picker.pressed = Some(0);
|
||||
assert!(ui.close_icon_picker());
|
||||
assert!(!ui.icon_picker.open);
|
||||
assert!(!ui.icon_picker_replace_selection);
|
||||
assert!(ui.icon_picker_search.is_empty());
|
||||
assert!(!ui.icon_picker_select_all);
|
||||
assert_eq!(ui.icon_picker.hover, None);
|
||||
assert_eq!(ui.icon_picker.pressed, None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
//! State-layer mirror of the icon-picker panel's buttons.
|
||||
//!
|
||||
//! The panel's click enum (`IconPickerHit`) carries owned `String`
|
||||
//! collection/name ids for a row select, so it isn't `Copy`. The row
|
||||
//! list is index-addressable (paint + hit-test share the same row
|
||||
//! order), so `Row(usize)` captures the hovered row without strings.
|
||||
//! Same wasm32-clean discipline as the other `*_state` mirrors.
|
||||
|
||||
/// Which icon-picker target the cursor is over. `None` = no hover wash.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IconPickerButton {
|
||||
/// The header `✕` close button.
|
||||
Close,
|
||||
/// An icon row, by its index into the visible list.
|
||||
Row(usize),
|
||||
/// The "load more" row at the foot of a remote result set.
|
||||
LoadMore,
|
||||
}
|
||||
|
|
@ -42,7 +42,6 @@ pub mod git_button_state;
|
|||
pub mod grouping;
|
||||
pub mod history;
|
||||
pub mod host_support;
|
||||
pub mod icon_picker_button_state;
|
||||
pub mod icon_picker_state;
|
||||
pub mod image_node_props;
|
||||
pub mod image_panel_state;
|
||||
|
|
@ -173,7 +172,6 @@ pub use fills::{
|
|||
pub use geometry::{aggregate_bounds, own_bounds, union_aggregate_bounds, DocRect};
|
||||
pub use git_button_state::GitButton;
|
||||
pub use history::{EditorSnapshot, History, HISTORY_CAP};
|
||||
pub use icon_picker_button_state::IconPickerButton;
|
||||
pub use icon_picker_state::{IconPickerRemoteIcon, IconPickerRemoteState, IconifyLoadMoreRequest};
|
||||
pub use image_node_props::image_node_summary;
|
||||
pub use instance_override::{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ use op_editor_core::{EditorState, IconPickerRemoteIcon, Locale};
|
|||
|
||||
pub const ICON_PICKER_PANEL_W: f32 = 320.0;
|
||||
pub const ICON_PICKER_PANEL_H: f32 = 420.0;
|
||||
pub const ICON_PICKER_CLOSE_HOVER: usize = usize::MAX - 1;
|
||||
pub const ICON_PICKER_LOAD_MORE_HOVER: usize = usize::MAX;
|
||||
const PAD: f32 = 14.0;
|
||||
const HEADER_H: f32 = 40.0;
|
||||
const SEARCH_H: f32 = 42.0;
|
||||
|
|
@ -57,36 +59,31 @@ pub struct IconPickerPanel<'a> {
|
|||
theme: Theme,
|
||||
locale: Locale,
|
||||
/// Which target the cursor is over — drives the hover wash.
|
||||
hover: Option<op_editor_core::IconPickerButton>,
|
||||
hover: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a> IconPickerPanel<'a> {
|
||||
pub fn for_editor(state: &'a EditorState) -> Option<IconPickerPanel<'a>> {
|
||||
if !state.editor_ui.icon_picker_open {
|
||||
if !state.editor_ui.icon_picker.open {
|
||||
return None;
|
||||
}
|
||||
Some(IconPickerPanel {
|
||||
state,
|
||||
theme: theme_for(&state.editor_ui),
|
||||
locale: state.editor_ui.locale,
|
||||
hover: state.editor_ui.icon_picker_hover,
|
||||
hover: state.editor_ui.icon_picker.hover,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolve a pointer to a hoverable target (close / row / load-more).
|
||||
/// Mirrors [`Self::hit_test`]'s row math; rows are index-addressable
|
||||
/// so no string ids leak into the hover state.
|
||||
pub fn hover_at(
|
||||
&self,
|
||||
panel: Rect,
|
||||
point: Point2D,
|
||||
) -> Option<op_editor_core::IconPickerButton> {
|
||||
use op_editor_core::IconPickerButton as B;
|
||||
pub fn hover_at(&self, panel: Rect, point: Point2D) -> Option<usize> {
|
||||
if !(panel).contains(point) {
|
||||
return None;
|
||||
}
|
||||
if (Self::close_rect(panel)).contains(point) {
|
||||
return Some(B::Close);
|
||||
return Some(ICON_PICKER_CLOSE_HOVER);
|
||||
}
|
||||
if point.y <= panel.origin.y + HEADER_H {
|
||||
return None;
|
||||
|
|
@ -99,10 +96,10 @@ impl<'a> IconPickerPanel<'a> {
|
|||
let item_cap = capacity.saturating_sub(usize::from(has_more));
|
||||
let items = self.rows(item_cap);
|
||||
if row < items.len() {
|
||||
return Some(B::Row(row));
|
||||
return Some(row);
|
||||
}
|
||||
if has_more && row == items.len() && !self.state.editor_ui.icon_picker_remote.loading {
|
||||
return Some(B::LoadMore);
|
||||
return Some(ICON_PICKER_LOAD_MORE_HOVER);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
@ -245,7 +242,7 @@ impl<'a> IconPickerPanel<'a> {
|
|||
Point2D::new(panel.origin.x + PAD, panel.origin.y + 25.0),
|
||||
);
|
||||
let close = Self::close_rect(panel);
|
||||
let close_hovered = self.hover == Some(op_editor_core::IconPickerButton::Close);
|
||||
let close_hovered = self.hover == Some(ICON_PICKER_CLOSE_HOVER);
|
||||
if close_hovered {
|
||||
cx.backend
|
||||
.fill_round_rect(close, 6.0, self.theme.button_hover);
|
||||
|
|
@ -332,7 +329,7 @@ impl<'a> IconPickerPanel<'a> {
|
|||
size: Point2D::new(panel.size.x - 12.0, ROW_H),
|
||||
};
|
||||
cx.backend.fill_round_rect(row, 6.0, self.theme.popover);
|
||||
if self.hover == Some(op_editor_core::IconPickerButton::Row(idx)) {
|
||||
if self.hover == Some(idx) {
|
||||
cx.backend
|
||||
.fill_round_rect(row, 6.0, self.theme.button_hover);
|
||||
}
|
||||
|
|
@ -381,7 +378,7 @@ impl<'a> IconPickerPanel<'a> {
|
|||
size: Point2D::new(panel.size.x - 12.0, ROW_H - 4.0),
|
||||
};
|
||||
cx.backend.fill_round_rect(row, 6.0, self.theme.muted);
|
||||
if self.hover == Some(op_editor_core::IconPickerButton::LoadMore) {
|
||||
if self.hover == Some(ICON_PICKER_LOAD_MORE_HOVER) {
|
||||
cx.backend
|
||||
.fill_round_rect(row, 6.0, self.theme.button_hover);
|
||||
}
|
||||
|
|
@ -420,3 +417,35 @@ fn truncate(text: &str, max_chars: usize) -> String {
|
|||
out.push_str("...");
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hover_at_uses_select_state_indices_for_close_and_rows() {
|
||||
let mut state = EditorState::default();
|
||||
state.editor_ui.open_icon_picker(false);
|
||||
let panel = IconPickerPanel::for_editor(&state).unwrap();
|
||||
let rect = Rect::xywh(0.0, 0.0, ICON_PICKER_PANEL_W, ICON_PICKER_PANEL_H);
|
||||
let close = IconPickerPanel::close_rect(rect);
|
||||
|
||||
assert_eq!(
|
||||
panel.hover_at(
|
||||
rect,
|
||||
Point2D::new(close.origin.x + close.size.x / 2.0, close.origin.y + 4.0)
|
||||
),
|
||||
Some(ICON_PICKER_CLOSE_HOVER)
|
||||
);
|
||||
assert_eq!(
|
||||
panel.hover_at(
|
||||
rect,
|
||||
Point2D::new(
|
||||
rect.origin.x + 20.0,
|
||||
IconPickerPanel::list_top(rect) + ROW_H / 2.0
|
||||
)
|
||||
),
|
||||
Some(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ impl WidgetHostNative {
|
|||
};
|
||||
match hit {
|
||||
IconPickerHit::Close => {
|
||||
self.editor_state.editor_ui.icon_picker_open = false;
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_hover = None;
|
||||
self.editor_state.editor_ui.close_icon_picker();
|
||||
}
|
||||
IconPickerHit::DragHeader => {
|
||||
self.icon_picker_drag = Some(IconPickerDragState {
|
||||
|
|
@ -84,10 +81,7 @@ impl WidgetHostNative {
|
|||
doc.x as f64,
|
||||
doc.y as f64,
|
||||
);
|
||||
self.editor_state.editor_ui.icon_picker_open = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_hover = None;
|
||||
self.editor_state.editor_ui.close_icon_picker();
|
||||
if inserted.is_some() {
|
||||
self.mark_dirty();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ impl WidgetHostNative {
|
|||
|| ui.variable_row_focus.is_some()
|
||||
|| self.editor_state.ui.property_focus.is_some()
|
||||
|| self.editor_state.chat.focused
|
||||
|| ui.icon_picker_open
|
||||
|| ui.icon_picker.open
|
||||
|| ui.component_browser_open
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl WidgetHostNative {
|
|||
|| self.variables_search_active()
|
||||
|| self.editor_state.editor_ui.effect_param_focus.is_some()
|
||||
|| self.editor_state.editor_ui.agent_settings.focus.is_some()
|
||||
|| self.editor_state.editor_ui.icon_picker_open
|
||||
|| self.editor_state.editor_ui.icon_picker.open
|
||||
|| self.editor_state.editor_ui.chat_model_picker.open
|
||||
|| self.editor_state.editor_ui.component_browser_open
|
||||
|| self.editor_state.chat.focused
|
||||
|
|
@ -472,15 +472,15 @@ impl WidgetHostNative {
|
|||
return true;
|
||||
}
|
||||
// Icon-picker panel hover (close / icon rows / load-more).
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
use op_editor_ui::widgets::icon_picker_panel::IconPickerPanel;
|
||||
if let Some(panel_rect) =
|
||||
self.icon_picker_panel_rect(self.last_viewport_w, self.last_viewport_h)
|
||||
{
|
||||
let new_hover = IconPickerPanel::for_editor(&self.editor_state)
|
||||
.and_then(|p| p.hover_at(panel_rect, Point2D::new(x, y)));
|
||||
if new_hover != self.editor_state.editor_ui.icon_picker_hover {
|
||||
self.editor_state.editor_ui.icon_picker_hover = new_hover;
|
||||
if new_hover != self.editor_state.editor_ui.icon_picker.hover {
|
||||
self.editor_state.editor_ui.icon_picker.hover = new_hover;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1824,13 +1824,13 @@ fn shape_picker_icon_row_opens_icon_picker() {
|
|||
assert!(host.apply_press(x, icon_row_y, viewport_w, viewport_h));
|
||||
|
||||
assert!(!host.editor_state().editor_ui.shape_picker.open);
|
||||
assert!(host.editor_state().editor_ui.icon_picker_open);
|
||||
assert!(host.editor_state().editor_ui.icon_picker.open);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn icon_picker_open_owns_keyboard_search() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut().editor_ui.icon_picker_open = true;
|
||||
host.editor_state_mut().editor_ui.icon_picker.open = true;
|
||||
|
||||
assert!(host.input_active_pub());
|
||||
assert!(host.apply_text('h'));
|
||||
|
|
@ -1839,7 +1839,7 @@ fn icon_picker_open_owns_keyboard_search() {
|
|||
assert!(host.apply_backspace());
|
||||
assert_eq!(host.editor_state().editor_ui.icon_picker_search, "h");
|
||||
assert!(host.apply_escape());
|
||||
assert!(!host.editor_state().editor_ui.icon_picker_open);
|
||||
assert!(!host.editor_state().editor_ui.icon_picker.open);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1847,7 +1847,7 @@ fn icon_picker_click_inserts_icon_font_node() {
|
|||
let mut host = WidgetHostNative::new();
|
||||
let viewport_w = 1440.0;
|
||||
let viewport_h = 900.0;
|
||||
host.editor_state_mut().editor_ui.icon_picker_open = true;
|
||||
host.editor_state_mut().editor_ui.icon_picker.open = true;
|
||||
host.editor_state_mut().editor_ui.icon_picker_search = "home".to_string();
|
||||
|
||||
let panel = host
|
||||
|
|
@ -1856,7 +1856,7 @@ fn icon_picker_click_inserts_icon_font_node() {
|
|||
let row_y = panel.origin.y + 40.0 + 42.0 + 20.0;
|
||||
assert!(host.apply_press(panel.origin.x + 40.0, row_y, viewport_w, viewport_h));
|
||||
|
||||
assert!(!host.editor_state().editor_ui.icon_picker_open);
|
||||
assert!(!host.editor_state().editor_ui.icon_picker.open);
|
||||
let icon = host
|
||||
.editor_state()
|
||||
.doc
|
||||
|
|
@ -1877,7 +1877,7 @@ fn icon_picker_header_drag_moves_the_panel() {
|
|||
let mut host = WidgetHostNative::new();
|
||||
let viewport_w = 1440.0;
|
||||
let viewport_h = 900.0;
|
||||
host.editor_state_mut().editor_ui.icon_picker_open = true;
|
||||
host.editor_state_mut().editor_ui.icon_picker.open = true;
|
||||
|
||||
let start = host
|
||||
.icon_picker_panel_rect(viewport_w, viewport_h)
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ fn context_menu_detach_component_sheds_reusable_flag() {
|
|||
fn remote_icon_insert_bakes_svg_d_as_path_node() {
|
||||
let mut host = seeded_host();
|
||||
let state = host.editor_state_mut();
|
||||
state.editor_ui.icon_picker_open = true;
|
||||
state.editor_ui.icon_picker.open = true;
|
||||
state.editor_ui.icon_picker_replace_selection = false;
|
||||
state.editor_ui.icon_picker_panel_pos = Some((0.0, 0.0));
|
||||
state.editor_ui.icon_picker_search = "zwxq".to_string();
|
||||
|
|
|
|||
|
|
@ -341,12 +341,16 @@ impl WidgetHostNative {
|
|||
if self.apply_image_panel_text(c) {
|
||||
return true;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_open && !c.is_control() {
|
||||
if self.editor_state.editor_ui.icon_picker.open && !c.is_control() {
|
||||
if self.editor_state.editor_ui.icon_picker_select_all {
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_select_all = false;
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
}
|
||||
self.editor_state.editor_ui.icon_picker_search.push(c);
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -644,10 +648,12 @@ impl WidgetHostNative {
|
|||
if self.apply_image_panel_backspace() {
|
||||
return true;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
if self.editor_state.editor_ui.icon_picker_select_all {
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_select_all = false;
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -658,6 +664,8 @@ impl WidgetHostNative {
|
|||
.pop()
|
||||
.is_some()
|
||||
{
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -859,7 +867,7 @@ impl WidgetHostNative {
|
|||
// would silently drop the node behind the focused field.
|
||||
if self.editor_state.ui.property_focus.is_some()
|
||||
|| self.editor_state.editor_ui.effect_param_focus.is_some()
|
||||
|| self.editor_state.editor_ui.icon_picker_open
|
||||
|| self.editor_state.editor_ui.icon_picker.open
|
||||
|| self.editor_state.editor_ui.chat_model_picker.open
|
||||
|| self.editor_state.editor_ui.component_browser_open
|
||||
|| self.editor_state.chat.focused
|
||||
|
|
@ -1493,12 +1501,8 @@ impl WidgetHostNative {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
self.editor_state.editor_ui.icon_picker_open = false;
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_select_all = false;
|
||||
self.editor_state.editor_ui.icon_picker_hover = None;
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
self.editor_state.editor_ui.close_icon_picker();
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ impl WidgetHostNative {
|
|||
viewport_w: f32,
|
||||
viewport_h: f32,
|
||||
) -> Option<Rect> {
|
||||
if !self.editor_state.editor_ui.icon_picker_open {
|
||||
if !self.editor_state.editor_ui.icon_picker.open {
|
||||
return None;
|
||||
}
|
||||
let ui = &self.editor_state.editor_ui;
|
||||
|
|
|
|||
|
|
@ -162,9 +162,7 @@ impl WidgetHostNative {
|
|||
}
|
||||
A::OpenSelectedIconPicker => {
|
||||
let ui = &mut self.editor_state.editor_ui;
|
||||
ui.icon_picker_open = true;
|
||||
ui.icon_picker_replace_selection = true;
|
||||
ui.icon_picker_search.clear();
|
||||
ui.open_icon_picker(true);
|
||||
ui.close_fill_type_picker();
|
||||
ui.image_fill_popover_open = false;
|
||||
ui.close_font_picker();
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ impl WidgetHostNative {
|
|||
self.editor_state.tool = tool;
|
||||
}
|
||||
ShapeChoice::OpenIconPicker => {
|
||||
self.editor_state.editor_ui.icon_picker_open = true;
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.open_icon_picker(false);
|
||||
}
|
||||
ShapeChoice::ImportImageOrSvg => {
|
||||
self.editor_state.editor_ui.pending_file_action =
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ impl WidgetHostNative {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
self.editor_state.editor_ui.icon_picker_select_all = true;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -535,7 +535,7 @@ fn focused_input(state: &EditorState) -> Option<(String, String, String)> {
|
|||
state.ui.property_input.text().to_owned(),
|
||||
));
|
||||
}
|
||||
if ui.icon_picker_open {
|
||||
if ui.icon_picker.open {
|
||||
return Some((
|
||||
"icon-search".to_string(),
|
||||
"Icon search".to_string(),
|
||||
|
|
@ -698,7 +698,7 @@ mod tests {
|
|||
#[test]
|
||||
fn focused_input_reports_open_search_popovers() {
|
||||
let mut state = EditorState::new();
|
||||
state.editor_ui.icon_picker_open = true;
|
||||
state.editor_ui.icon_picker.open = true;
|
||||
state.editor_ui.icon_picker_search = "arrow".to_string();
|
||||
let (key, _label, value) = focused_input(&state).expect("icon search");
|
||||
assert_eq!(key, "icon-search");
|
||||
|
|
|
|||
|
|
@ -33,10 +33,7 @@ impl WidgetHost {
|
|||
};
|
||||
match hit {
|
||||
IconPickerHit::Close => {
|
||||
self.editor_state.editor_ui.icon_picker_open = false;
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_hover = None;
|
||||
self.editor_state.editor_ui.close_icon_picker();
|
||||
}
|
||||
IconPickerHit::DragHeader => {
|
||||
self.icon_picker_drag = Some(PanelDragState {
|
||||
|
|
@ -93,10 +90,7 @@ impl WidgetHost {
|
|||
doc.x as f64,
|
||||
doc.y as f64,
|
||||
);
|
||||
self.editor_state.editor_ui.icon_picker_open = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_hover = None;
|
||||
self.editor_state.editor_ui.close_icon_picker();
|
||||
if inserted.is_some() {
|
||||
self.mark_dirty();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -688,12 +688,8 @@ impl WidgetHost {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
self.editor_state.editor_ui.icon_picker_open = false;
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_select_all = false;
|
||||
self.editor_state.editor_ui.icon_picker_hover = None;
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
self.editor_state.editor_ui.close_icon_picker();
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ impl WidgetHost {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
self.editor_state.editor_ui.icon_picker_select_all = true;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl WidgetHost {
|
|||
.is_some()
|
||||
|| self.variables_search_active()
|
||||
|| self.editor_state.editor_ui.agent_settings.focus.is_some()
|
||||
|| self.editor_state.editor_ui.icon_picker_open
|
||||
|| self.editor_state.editor_ui.icon_picker.open
|
||||
|| self.editor_state.editor_ui.chat_model_picker.open
|
||||
|| self.editor_state.editor_ui.component_browser_open
|
||||
|| self.editor_state.chat.focused
|
||||
|
|
|
|||
|
|
@ -89,15 +89,15 @@ impl WidgetHost {
|
|||
return true;
|
||||
}
|
||||
// Icon-picker panel hover (close / icon rows / load-more).
|
||||
if self.editor_state.editor_ui.icon_picker_open {
|
||||
if self.editor_state.editor_ui.icon_picker.open {
|
||||
use op_editor_ui::widgets::icon_picker_panel::IconPickerPanel;
|
||||
if let Some(panel_rect) =
|
||||
self.icon_picker_panel_rect(self.last_viewport_w, self.last_viewport_h)
|
||||
{
|
||||
let new_hover = IconPickerPanel::for_editor(&self.editor_state)
|
||||
.and_then(|p| p.hover_at(panel_rect, Point2D::new(x, y)));
|
||||
if new_hover != self.editor_state.editor_ui.icon_picker_hover {
|
||||
self.editor_state.editor_ui.icon_picker_hover = new_hover;
|
||||
if new_hover != self.editor_state.editor_ui.icon_picker.hover {
|
||||
self.editor_state.editor_ui.icon_picker.hover = new_hover;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,18 @@ impl WidgetHost {
|
|||
/// Icon-picker search box typing — owns every printable char
|
||||
/// while the panel is open.
|
||||
pub(in crate::widget_host) fn icon_picker_text(&mut self, c: char) -> Option<bool> {
|
||||
if !self.editor_state.editor_ui.icon_picker_open || c.is_control() {
|
||||
if !self.editor_state.editor_ui.icon_picker.open || c.is_control() {
|
||||
return None;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_select_all {
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_select_all = false;
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
}
|
||||
self.editor_state.editor_ui.icon_picker_search.push(c);
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
self.mark_dirty();
|
||||
Some(true)
|
||||
}
|
||||
|
|
@ -29,12 +33,14 @@ impl WidgetHost {
|
|||
/// Icon-picker search box backspace — swallows the key while the
|
||||
/// panel is open even when the draft is already empty.
|
||||
pub(in crate::widget_host) fn icon_picker_backspace(&mut self) -> Option<bool> {
|
||||
if !self.editor_state.editor_ui.icon_picker_open {
|
||||
if !self.editor_state.editor_ui.icon_picker.open {
|
||||
return None;
|
||||
}
|
||||
if self.editor_state.editor_ui.icon_picker_select_all {
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.icon_picker_select_all = false;
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
self.mark_dirty();
|
||||
return Some(true);
|
||||
}
|
||||
|
|
@ -45,6 +51,8 @@ impl WidgetHost {
|
|||
.pop()
|
||||
.is_some()
|
||||
{
|
||||
self.editor_state.editor_ui.icon_picker.hover = None;
|
||||
self.editor_state.editor_ui.icon_picker.pressed = None;
|
||||
self.mark_dirty();
|
||||
return Some(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ fn shape_picker_icon_row_opens_icon_picker_panel() {
|
|||
let row_y = row_y.expect("icon row present in the shape picker");
|
||||
|
||||
assert!(host.apply_press(x, row_y, W, H));
|
||||
assert!(host.editor_state.editor_ui.icon_picker_open);
|
||||
assert!(host.editor_state.editor_ui.icon_picker.open);
|
||||
assert!(!host.editor_state.editor_ui.shape_picker.open);
|
||||
|
||||
// The icon-picker panel paints at its centred rect.
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ impl WidgetHost {
|
|||
viewport_w: f32,
|
||||
viewport_h: f32,
|
||||
) -> Option<Rect> {
|
||||
if !self.editor_state.editor_ui.icon_picker_open {
|
||||
if !self.editor_state.editor_ui.icon_picker.open {
|
||||
return None;
|
||||
}
|
||||
let ui = &self.editor_state.editor_ui;
|
||||
|
|
|
|||
|
|
@ -122,9 +122,7 @@ impl WidgetHost {
|
|||
// Property-panel icon section → replace-selection
|
||||
// picker (mirrors the native host's arm).
|
||||
let ui = &mut self.editor_state.editor_ui;
|
||||
ui.icon_picker_open = true;
|
||||
ui.icon_picker_replace_selection = true;
|
||||
ui.icon_picker_search.clear();
|
||||
ui.open_icon_picker(true);
|
||||
ui.close_fill_type_picker();
|
||||
ui.image_fill_popover_open = false;
|
||||
ui.close_font_picker();
|
||||
|
|
|
|||
|
|
@ -34,9 +34,7 @@ impl WidgetHost {
|
|||
self.editor_state.tool = tool;
|
||||
}
|
||||
ShapeChoice::OpenIconPicker => {
|
||||
self.editor_state.editor_ui.icon_picker_open = true;
|
||||
self.editor_state.editor_ui.icon_picker_replace_selection = false;
|
||||
self.editor_state.editor_ui.icon_picker_search.clear();
|
||||
self.editor_state.editor_ui.open_icon_picker(false);
|
||||
}
|
||||
ShapeChoice::ImportImageOrSvg => {
|
||||
// No file-picker service on web yet — raise the same
|
||||
|
|
|
|||
Loading…
Reference in a new issue