refactor(ui): adopt iced color picker
Remove local Pin/PickList wrappers and the ACI grid.\nBlock viewport input behind the picker and load iced_aw glyphs.
This commit is contained in:
parent
3ab8dc3ca5
commit
c9c3d3b1bf
25 changed files with 289 additions and 255 deletions
|
|
@ -52,7 +52,7 @@ iced = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00
|
||||||
iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
|
iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
|
||||||
# iced_aw 0.15 compatibility is pending upstream in PR #432. Pin the exact
|
# iced_aw 0.15 compatibility is pending upstream in PR #432. Pin the exact
|
||||||
# reviewed pair of commits so Iced and its additional widgets cannot drift.
|
# reviewed pair of commits so Iced and its additional widgets cannot drift.
|
||||||
iced_aw = { git = "https://github.com/tsuza/iced_aw.git", rev = "a9301708a4d008dc17f63cbbbb2b2aa06e0e7352", features = ["menu", "context_menu"] }
|
iced_aw = { git = "https://github.com/tsuza/iced_aw.git", rev = "a9301708a4d008dc17f63cbbbb2b2aa06e0e7352", features = ["menu", "context_menu", "color_picker"] }
|
||||||
bytemuck = { version = "1.25", features = ["derive"] }
|
bytemuck = { version = "1.25", features = ["derive"] }
|
||||||
glam = { version = "0.33", features = ["bytemuck"] }
|
glam = { version = "0.33", features = ["bytemuck"] }
|
||||||
truck-modeling = "0.6"
|
truck-modeling = "0.6"
|
||||||
|
|
|
||||||
|
|
@ -465,8 +465,8 @@ pub(super) struct OpenCADStudio {
|
||||||
/// OS window Id of the primary application window.
|
/// OS window Id of the primary application window.
|
||||||
main_window: Option<window::Id>,
|
main_window: Option<window::Id>,
|
||||||
// ── Floating panel windows ────────────────────────────────────────────
|
// ── Floating panel windows ────────────────────────────────────────────
|
||||||
/// Standalone "Select Color" palette window + the field it targets.
|
/// Active `iced_aw` colour picker: destination plus its initial colour.
|
||||||
color_pick_target: Option<ColorPickTarget>,
|
color_pick_target: Option<(ColorPickTarget, AcadColor)>,
|
||||||
/// The open in-canvas modal dialog, if any (Plan B: shared overlay instead
|
/// The open in-canvas modal dialog, if any (Plan B: shared overlay instead
|
||||||
/// of OS windows).
|
/// of OS windows).
|
||||||
active_modal: Option<ModalKind>,
|
active_modal: Option<ModalKind>,
|
||||||
|
|
@ -2503,11 +2503,11 @@ pub enum Message {
|
||||||
DsToggle(DsField),
|
DsToggle(DsField),
|
||||||
/// Toggle the expanded colour palette for a DimStyle colour field.
|
/// Toggle the expanded colour palette for a DimStyle colour field.
|
||||||
DsColorMore(DsField),
|
DsColorMore(DsField),
|
||||||
/// Open the standalone palette window targeting the given field.
|
/// Open the `iced_aw` colour picker for a field and its current colour.
|
||||||
OpenColorWindow(ColorPickTarget),
|
OpenColorWindow(ColorPickTarget, AcadColor),
|
||||||
/// Close the nested colour-picker modal without choosing (Plan B).
|
/// Close the colour picker without choosing.
|
||||||
CloseColorPicker,
|
CloseColorPicker,
|
||||||
/// A colour was chosen in the standalone palette window.
|
/// A colour was chosen in the `iced_aw` picker.
|
||||||
ColorWindowPick(acadrust::types::Color),
|
ColorWindowPick(acadrust::types::Color),
|
||||||
/// Set a block/linetype Handle field on the selected dim style from a
|
/// Set a block/linetype Handle field on the selected dim style from a
|
||||||
/// dropdown of available block-records / linetypes (by name).
|
/// dropdown of available block-records / linetypes (by name).
|
||||||
|
|
@ -3177,6 +3177,7 @@ pub fn run() -> iced::Result {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.theme(|state: &OpenCADStudio, _| state.active_theme.clone())
|
.theme(|state: &OpenCADStudio, _| state.active_theme.clone())
|
||||||
|
.font(iced_aw::ICED_AW_FONT_BYTES)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3206,5 +3207,6 @@ pub fn run_web() -> iced::Result {
|
||||||
.subscription(OpenCADStudio::subscription)
|
.subscription(OpenCADStudio::subscription)
|
||||||
.title(|_state: &OpenCADStudio| "Open CAD Studio".to_string())
|
.title(|_state: &OpenCADStudio| "Open CAD Studio".to_string())
|
||||||
.theme(|state: &OpenCADStudio| state.active_theme.clone())
|
.theme(|state: &OpenCADStudio| state.active_theme.clone())
|
||||||
|
.font(iced_aw::ICED_AW_FONT_BYTES)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2120,6 +2120,9 @@ impl OpenCADStudio {
|
||||||
Message::PaneClicked(pane) => self.on_pane_clicked(pane),
|
Message::PaneClicked(pane) => self.on_pane_clicked(pane),
|
||||||
Message::PaneDragged(ev) => self.on_pane_dragged(ev),
|
Message::PaneDragged(ev) => self.on_pane_dragged(ev),
|
||||||
Message::PaneMove(idx, local) => {
|
Message::PaneMove(idx, local) => {
|
||||||
|
if self.color_pick_target.is_some() {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
let p = self.pane_canvas_point(idx, local);
|
let p = self.pane_canvas_point(idx, local);
|
||||||
// While dragging a pane, just track the cursor (no focus swap or
|
// While dragging a pane, just track the cursor (no focus swap or
|
||||||
// snap) so the drop target reads cleanly.
|
// snap) so the drop target reads cleanly.
|
||||||
|
|
@ -5744,8 +5747,8 @@ impl OpenCADStudio {
|
||||||
};
|
};
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::OpenColorWindow(target) => {
|
Message::OpenColorWindow(target, color) => {
|
||||||
self.color_pick_target = Some(target);
|
self.color_pick_target = Some((target, color));
|
||||||
self.ds_color_open = None;
|
self.ds_color_open = None;
|
||||||
self.mls_color_open = None;
|
self.mls_color_open = None;
|
||||||
self.ts_color_open = None;
|
self.ts_color_open = None;
|
||||||
|
|
@ -5753,8 +5756,7 @@ impl OpenCADStudio {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.tabs[i].properties.color_picker_open = false;
|
self.tabs[i].properties.color_picker_open = false;
|
||||||
self.tabs[i].layers.color_picker_row = None;
|
self.tabs[i].layers.color_picker_row = None;
|
||||||
// Shown as a nested modal over the active dialog (Plan B):
|
// `color_pick_target.is_some()` drives the iced_aw overlay.
|
||||||
// `color_pick_target.is_some()` drives the overlay in view_main.
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::CloseColorPicker => {
|
Message::CloseColorPicker => {
|
||||||
|
|
|
||||||
|
|
@ -960,7 +960,7 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
||||||
|
|
||||||
pub(super) fn on_color_window_pick(&mut self, color: acadrust::types::Color) -> Task<Message> {
|
pub(super) fn on_color_window_pick(&mut self, color: acadrust::types::Color) -> Task<Message> {
|
||||||
let s = crate::ui::color_select::color_to_aci_string(color);
|
let s = crate::ui::color_select::color_to_aci_string(color);
|
||||||
let edit = match self.color_pick_target.take() {
|
let edit = match self.color_pick_target.take().map(|(target, _)| target) {
|
||||||
Some(crate::app::ColorPickTarget::DimStyle(f)) => Some(Message::DsEdit(f, s)),
|
Some(crate::app::ColorPickTarget::DimStyle(f)) => Some(Message::DsEdit(f, s)),
|
||||||
Some(crate::app::ColorPickTarget::MLeader(f)) => {
|
Some(crate::app::ColorPickTarget::MLeader(f)) => {
|
||||||
Some(Message::MLeaderStyleEdit { field: f, value: s })
|
Some(Message::MLeaderStyleEdit { field: f, value: s })
|
||||||
|
|
@ -986,10 +986,14 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
||||||
}
|
}
|
||||||
Some(crate::app::ColorPickTarget::Layer(idx)) => {
|
Some(crate::app::ColorPickTarget::Layer(idx)) => {
|
||||||
self.tabs[self.active_tab].layers.selected = Some(idx);
|
self.tabs[self.active_tab].layers.selected = Some(idx);
|
||||||
match color {
|
let index = match color {
|
||||||
acadrust::types::Color::Index(i) => Some(Message::LayerColorSet(i)),
|
acadrust::types::Color::Index(i) => i,
|
||||||
_ => None,
|
acadrust::types::Color::Rgb { r, g, b } => {
|
||||||
}
|
crate::ui::color_select::nearest_aci(r, g, b)
|
||||||
|
}
|
||||||
|
_ => 7,
|
||||||
|
};
|
||||||
|
Some(Message::LayerColorSet(index))
|
||||||
}
|
}
|
||||||
Some(crate::app::ColorPickTarget::LayerState(idx)) => {
|
Some(crate::app::ColorPickTarget::LayerState(idx)) => {
|
||||||
Some(Message::LayerStateEditorLayerColor(idx, color))
|
Some(Message::LayerStateEditorLayerColor(idx, color))
|
||||||
|
|
|
||||||
|
|
@ -435,6 +435,10 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn on_cursor_moved(&mut self, p: Point) -> Task<Message> {
|
pub(super) fn on_cursor_moved(&mut self, p: Point) -> Task<Message> {
|
||||||
|
if self.color_pick_target.is_some() {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
|
||||||
// `p` is relative to the ViewCube hit area's top-left. Map
|
// `p` is relative to the ViewCube hit area's top-left. Map
|
||||||
// it back to full-canvas coordinates so ViewportClick's
|
// it back to full-canvas coordinates so ViewportClick's
|
||||||
// hit-test lines up. The hit area sits in the top-right of
|
// hit-test lines up. The hit area sits in the top-right of
|
||||||
|
|
@ -498,7 +502,7 @@ impl OpenCADStudio {
|
||||||
// leaks through the stack to the pane mouse_area beneath and
|
// leaks through the stack to the pane mouse_area beneath and
|
||||||
// would track the crosshair over the dropdown's empty areas.
|
// would track the crosshair over the dropdown's empty areas.
|
||||||
// Drop the move here instead. (#227)
|
// Drop the move here instead. (#227)
|
||||||
if self.ribbon.open_dropdown.is_some() {
|
if self.ribbon.open_dropdown.is_some() || self.color_pick_target.is_some() {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,12 @@ pub(super) fn viewport_controls<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render-mode picker, restyled borderless so the outer chip frames it.
|
// Render-mode picker, restyled borderless so the outer chip frames it.
|
||||||
let picker = crate::ui::pick_list(
|
let picker = iced::widget::pick_list(
|
||||||
render_modes,
|
|
||||||
Some(RenderModeChoice(render_mode)),
|
Some(RenderModeChoice(render_mode)),
|
||||||
|c| Message::SetRenderMode(c.0),
|
render_modes,
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(|c| Message::SetRenderMode(c.0))
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.padding([4, 6])
|
.padding([4, 6])
|
||||||
.style(move |theme: &Theme, _| {
|
.style(move |theme: &Theme, _| {
|
||||||
|
|
|
||||||
|
|
@ -788,10 +788,10 @@ impl OpenCADStudio {
|
||||||
.into();
|
.into();
|
||||||
// Pin the bar to the active model tile's top-left corner so it
|
// Pin the bar to the active model tile's top-left corner so it
|
||||||
// follows the active panel in a tiled layout.
|
// follows the active panel in a tiled layout.
|
||||||
let bar_layer = crate::ui::pin_at(
|
let bar_layer = iced::widget::pin(
|
||||||
iced::Point::new(rect.x, rect.y),
|
|
||||||
container(adaptive).width(iced::Length::Fixed(rect.width.max(1.0))),
|
container(adaptive).width(iced::Length::Fixed(rect.width.max(1.0))),
|
||||||
);
|
)
|
||||||
|
.position(iced::Point::new(rect.x.max(0.0), rect.y.max(0.0)));
|
||||||
viewport_stack = viewport_stack.push(bar_layer);
|
viewport_stack = viewport_stack.push(bar_layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -840,7 +840,8 @@ impl OpenCADStudio {
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let border_layer = crate::ui::pin_at(iced::Point::new(x, y), border_frame);
|
let border_layer =
|
||||||
|
iced::widget::pin(border_frame).position(iced::Point::new(x, y));
|
||||||
viewport_stack = viewport_stack.push(border_layer);
|
viewport_stack = viewport_stack.push(border_layer);
|
||||||
|
|
||||||
let vp_mode = tab
|
let vp_mode = tab
|
||||||
|
|
@ -866,10 +867,10 @@ impl OpenCADStudio {
|
||||||
])
|
])
|
||||||
.report_width0(self.render_bar_w.clone())
|
.report_width0(self.render_bar_w.clone())
|
||||||
.into();
|
.into();
|
||||||
let picker_layer = crate::ui::pin_at(
|
let picker_layer = iced::widget::pin(
|
||||||
iced::Point::new(x + 4.0, y + 4.0),
|
|
||||||
container(adaptive).width(iced::Length::Fixed(rect.width.max(1.0))),
|
container(adaptive).width(iced::Length::Fixed(rect.width.max(1.0))),
|
||||||
);
|
)
|
||||||
|
.position(iced::Point::new(x + 4.0, y + 4.0));
|
||||||
viewport_stack = viewport_stack.push(picker_layer);
|
viewport_stack = viewport_stack.push(picker_layer);
|
||||||
|
|
||||||
// Hide the ViewCube first — before the render bar — when they collide.
|
// Hide the ViewCube first — before the render bar — when they collide.
|
||||||
|
|
@ -877,10 +878,8 @@ impl OpenCADStudio {
|
||||||
let cube_x = (rect.x + rect.width - VIEWCUBE_HIT_SIZE - VIEWCUBE_PAD).max(0.0);
|
let cube_x = (rect.x + rect.width - VIEWCUBE_HIT_SIZE - VIEWCUBE_PAD).max(0.0);
|
||||||
let cube_y = (rect.y + VIEWCUBE_PAD).max(0.0);
|
let cube_y = (rect.y + VIEWCUBE_PAD).max(0.0);
|
||||||
|
|
||||||
let controls = crate::ui::pin_at(
|
let controls = iced::widget::pin(viewcube_nav_controls())
|
||||||
iced::Point::new(cube_x, cube_y),
|
.position(iced::Point::new(cube_x, cube_y));
|
||||||
viewcube_nav_controls(),
|
|
||||||
);
|
|
||||||
viewport_stack = viewport_stack.push(controls);
|
viewport_stack = viewport_stack.push(controls);
|
||||||
|
|
||||||
let ucs_current = tab
|
let ucs_current = tab
|
||||||
|
|
@ -896,13 +895,14 @@ impl OpenCADStudio {
|
||||||
.map(|u| u.name.clone())
|
.map(|u| u.name.clone())
|
||||||
.filter(|n| !n.is_empty())
|
.filter(|n| !n.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
let picker = crate::ui::pin_at(
|
let picker = iced::widget::pin(iced::widget::opaque(viewcube_ucs_picker(
|
||||||
iced::Point::new(
|
ucs_current,
|
||||||
|
ucs_names,
|
||||||
|
)))
|
||||||
|
.position(iced::Point::new(
|
||||||
cube_x + VIEWCUBE_HIT_SIZE * 0.5 - UCS_PICKER_W * 0.5,
|
cube_x + VIEWCUBE_HIT_SIZE * 0.5 - UCS_PICKER_W * 0.5,
|
||||||
cube_y + VIEWCUBE_HIT_SIZE + 6.0,
|
cube_y + VIEWCUBE_HIT_SIZE + 6.0,
|
||||||
),
|
));
|
||||||
iced::widget::opaque(viewcube_ucs_picker(ucs_current, ucs_names)),
|
|
||||||
);
|
|
||||||
viewport_stack = viewport_stack.push(picker);
|
viewport_stack = viewport_stack.push(picker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -917,10 +917,8 @@ impl OpenCADStudio {
|
||||||
let cube_y = (rect.y + VIEWCUBE_PAD).max(0.0);
|
let cube_y = (rect.y + VIEWCUBE_PAD).max(0.0);
|
||||||
|
|
||||||
// Cube hit area + nav controls (home / roll / nudge) as one layer.
|
// Cube hit area + nav controls (home / roll / nudge) as one layer.
|
||||||
let controls = crate::ui::pin_at(
|
let controls = iced::widget::pin(viewcube_nav_controls())
|
||||||
iced::Point::new(cube_x, cube_y),
|
.position(iced::Point::new(cube_x, cube_y));
|
||||||
viewcube_nav_controls(),
|
|
||||||
);
|
|
||||||
viewport_stack = viewport_stack.push(controls);
|
viewport_stack = viewport_stack.push(controls);
|
||||||
|
|
||||||
// WCS / named-UCS selector under the cube.
|
// WCS / named-UCS selector under the cube.
|
||||||
|
|
@ -937,13 +935,14 @@ impl OpenCADStudio {
|
||||||
.map(|u| u.name.clone())
|
.map(|u| u.name.clone())
|
||||||
.filter(|n| !n.is_empty())
|
.filter(|n| !n.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
let picker = crate::ui::pin_at(
|
let picker = iced::widget::pin(iced::widget::opaque(viewcube_ucs_picker(
|
||||||
iced::Point::new(
|
ucs_current,
|
||||||
|
ucs_names,
|
||||||
|
)))
|
||||||
|
.position(iced::Point::new(
|
||||||
cube_x + VIEWCUBE_HIT_SIZE * 0.5 - UCS_PICKER_W * 0.5,
|
cube_x + VIEWCUBE_HIT_SIZE * 0.5 - UCS_PICKER_W * 0.5,
|
||||||
cube_y + VIEWCUBE_HIT_SIZE + 6.0,
|
cube_y + VIEWCUBE_HIT_SIZE + 6.0,
|
||||||
),
|
));
|
||||||
iced::widget::opaque(viewcube_ucs_picker(ucs_current, ucs_names)),
|
|
||||||
);
|
|
||||||
viewport_stack = viewport_stack.push(picker);
|
viewport_stack = viewport_stack.push(picker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1554,21 +1553,24 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
None => composed.into(),
|
None => composed.into(),
|
||||||
};
|
};
|
||||||
// The colour picker is a nested modal: it stacks over whichever dialog
|
// iced_aw owns the colour-picker overlay and keeps it above whichever
|
||||||
// (style editor, properties, …) requested it.
|
// application modal requested it.
|
||||||
if self.color_pick_target.is_some() {
|
if let Some((_, current)) = self.color_pick_target.as_ref() {
|
||||||
crate::ui::modal::modal(
|
let initial = crate::ui::properties::acad_color_display(*current).0;
|
||||||
base,
|
let modal_base =
|
||||||
"Select Color",
|
crate::ui::modal::backdrop(base, Message::CloseColorPicker);
|
||||||
iced::widget::container(crate::ui::color_select::color_grid_window(
|
iced_aw::ColorPicker::new(
|
||||||
Message::ColorWindowPick,
|
true,
|
||||||
))
|
initial,
|
||||||
.width(iced::Length::Fit.max(420.0))
|
modal_base,
|
||||||
.height(iced::Length::Fit.max(470.0)),
|
|
||||||
Message::CloseColorPicker,
|
Message::CloseColorPicker,
|
||||||
iced::Vector::ZERO,
|
|color| {
|
||||||
crate::ui::modal::ModalOptions::MOVABLE_FIXED,
|
Message::ColorWindowPick(
|
||||||
|
crate::ui::color_select::iced_to_acad_color(color),
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
base
|
base
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -982,9 +982,12 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a,
|
||||||
items.push(
|
items.push(
|
||||||
row![
|
row![
|
||||||
label("Format:").width(70),
|
label("Format:").width(70),
|
||||||
crate::ui::pick_list(crate::io::SAVE_FORMAT_OPTIONS, sel_fmt, |s: &str| {
|
iced::widget::pick_list(
|
||||||
Message::SaveDialogFormatChanged(s.to_string())
|
sel_fmt,
|
||||||
})
|
crate::io::SAVE_FORMAT_OPTIONS,
|
||||||
|
|value| value.to_string(),
|
||||||
|
)
|
||||||
|
.on_select(|s: &str| Message::SaveDialogFormatChanged(s.to_string()))
|
||||||
.width(Fit),
|
.width(Fit),
|
||||||
]
|
]
|
||||||
.align_y(iced::Alignment::Center)
|
.align_y(iced::Alignment::Center)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ pub(super) fn position_canvas_overlay<'a>(
|
||||||
anchor: iced::Point,
|
anchor: iced::Point,
|
||||||
panel: Element<'a, Message>,
|
panel: Element<'a, Message>,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
crate::ui::pin_at(anchor, iced::widget::opaque(panel))
|
iced::widget::pin(iced::widget::opaque(panel))
|
||||||
|
.position(iced::Point::new(anchor.x.max(0.0), anchor.y.max(0.0)))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── In-place MText editor overlay ───────────────────────────────────────────
|
// ── In-place MText editor overlay ───────────────────────────────────────────
|
||||||
|
|
@ -369,7 +371,12 @@ pub(super) fn mtext_editor_overlay<'a>(
|
||||||
} else {
|
} else {
|
||||||
styles
|
styles
|
||||||
};
|
};
|
||||||
let style_pl = crate::ui::pick_list(style_opts, Some(ed.style.clone()), Message::MTextStyle)
|
let style_pl = iced::widget::pick_list(
|
||||||
|
Some(ed.style.clone()),
|
||||||
|
style_opts,
|
||||||
|
|value| value.to_string(),
|
||||||
|
)
|
||||||
|
.on_select(Message::MTextStyle)
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(iced::Length::Fixed(96.0));
|
.width(iced::Length::Fixed(96.0));
|
||||||
let font_sel = if ed.font.trim().is_empty() {
|
let font_sel = if ed.font.trim().is_empty() {
|
||||||
|
|
@ -377,14 +384,15 @@ pub(super) fn mtext_editor_overlay<'a>(
|
||||||
} else {
|
} else {
|
||||||
ed.font.clone()
|
ed.font.clone()
|
||||||
};
|
};
|
||||||
let font_pl = crate::ui::pick_list(
|
let font_pl = iced::widget::pick_list(
|
||||||
|
Some(font_sel),
|
||||||
MTEXT_FONTS
|
MTEXT_FONTS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
Some(font_sel),
|
|value| value.to_string(),
|
||||||
Message::MTextFont,
|
|
||||||
)
|
)
|
||||||
|
.on_select(Message::MTextFont)
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(iced::Length::Fixed(120.0));
|
.width(iced::Length::Fixed(120.0));
|
||||||
// Same colour picker as the Properties panel (named swatches + "More…" full
|
// Same colour picker as the Properties panel (named swatches + "More…" full
|
||||||
|
|
@ -398,7 +406,10 @@ pub(super) fn mtext_editor_overlay<'a>(
|
||||||
},
|
},
|
||||||
Message::MTextColorChanged,
|
Message::MTextColorChanged,
|
||||||
Message::MTextColorPickerToggle,
|
Message::MTextColorPickerToggle,
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::MText),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::MText,
|
||||||
|
acadrust::types::Color::from_index(ed.color_aci as i16),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.width(iced::Length::Fixed(150.0));
|
.width(iced::Length::Fixed(150.0));
|
||||||
|
|
||||||
|
|
@ -443,11 +454,12 @@ pub(super) fn mtext_editor_overlay<'a>(
|
||||||
.width(width);
|
.width(width);
|
||||||
|
|
||||||
// ── Row 2: oblique / width / char-spacing · align · line spacing · OK ─
|
// ── Row 2: oblique / width / char-spacing · align · line spacing · OK ─
|
||||||
let justify = crate::ui::pick_list(
|
let justify = iced::widget::pick_list(
|
||||||
JustifyChoice::ALL,
|
|
||||||
Some(JustifyChoice(ed.attachment)),
|
Some(JustifyChoice(ed.attachment)),
|
||||||
|c| Message::MTextJustify(c.0),
|
JustifyChoice::ALL,
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(|c| Message::MTextJustify(c.0))
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(iced::Length::Fixed(112.0));
|
.width(iced::Length::Fixed(112.0));
|
||||||
let row2 = row![
|
let row2 = row![
|
||||||
|
|
@ -1008,7 +1020,12 @@ pub(super) fn qselect_overlay<'a>(
|
||||||
Space::new().height(10),
|
Space::new().height(10),
|
||||||
row![
|
row![
|
||||||
label("Object type:"),
|
label("Object type:"),
|
||||||
crate::ui::pick_list(type_options, Some(type_sel), |s: String| {
|
iced::widget::pick_list(
|
||||||
|
Some(type_sel),
|
||||||
|
type_options,
|
||||||
|
|value| value.to_string(),
|
||||||
|
)
|
||||||
|
.on_select(|s: String| {
|
||||||
if s == QSELECT_ANY_TYPE {
|
if s == QSELECT_ANY_TYPE {
|
||||||
Message::QSelectSetType(None)
|
Message::QSelectSetType(None)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1022,17 +1039,18 @@ pub(super) fn qselect_overlay<'a>(
|
||||||
Space::new().height(6),
|
Space::new().height(6),
|
||||||
row![
|
row![
|
||||||
label("Property:"),
|
label("Property:"),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
prop_options,
|
|
||||||
Some(prop_sel),
|
Some(prop_sel),
|
||||||
|p: crate::app::QSelectPropertyChoice| {
|
prop_options,
|
||||||
|
|value| value.to_string(),
|
||||||
|
)
|
||||||
|
.on_select(|p: crate::app::QSelectPropertyChoice| {
|
||||||
if p.field.is_empty() {
|
if p.field.is_empty() {
|
||||||
Message::QSelectSetProperty(None)
|
Message::QSelectSetProperty(None)
|
||||||
} else {
|
} else {
|
||||||
Message::QSelectSetProperty(Some(p))
|
Message::QSelectSetProperty(Some(p))
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
.width(Fill),
|
.width(Fill),
|
||||||
]
|
]
|
||||||
.align_y(iced::Alignment::Center)
|
.align_y(iced::Alignment::Center)
|
||||||
|
|
@ -1040,11 +1058,12 @@ pub(super) fn qselect_overlay<'a>(
|
||||||
Space::new().height(6),
|
Space::new().height(6),
|
||||||
row![
|
row![
|
||||||
label("Operator:"),
|
label("Operator:"),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
op_options,
|
|
||||||
Some(state.operator),
|
Some(state.operator),
|
||||||
Message::QSelectSetOperator
|
op_options,
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(Message::QSelectSetOperator)
|
||||||
.width(Fill),
|
.width(Fill),
|
||||||
]
|
]
|
||||||
.align_y(iced::Alignment::Center)
|
.align_y(iced::Alignment::Center)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ use iced::{Background, Border, Element, Theme};
|
||||||
|
|
||||||
/// Place `el` at pixel offset (x, y) inside a fill layer (top-left origin).
|
/// Place `el` at pixel offset (x, y) inside a fill layer (top-left origin).
|
||||||
fn vc_place<'a>(x: f32, y: f32, el: Element<'a, Message>) -> Element<'a, Message> {
|
fn vc_place<'a>(x: f32, y: f32, el: Element<'a, Message>) -> Element<'a, Message> {
|
||||||
crate::ui::pin_at(iced::Point::new(x, y), el)
|
iced::widget::pin(el)
|
||||||
|
.position(iced::Point::new(x.max(0.0), y.max(0.0)))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borderless square icon button used by the ViewCube nav controls.
|
/// Borderless square icon button used by the ViewCube nav controls.
|
||||||
|
|
@ -155,7 +157,8 @@ pub(super) fn viewcube_ucs_picker<'a>(current: String, names: Vec<String>) -> El
|
||||||
} else {
|
} else {
|
||||||
current
|
current
|
||||||
};
|
};
|
||||||
crate::ui::pick_list(options, Some(selected), Message::SetViewcubeUcs)
|
iced::widget::pick_list(Some(selected), options, |value| value.to_string())
|
||||||
|
.on_select(Message::SetViewcubeUcs)
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
// Fixed width so the caller can centre it under the cube centre with a
|
// Fixed width so the caller can centre it under the cube centre with a
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use acadrust::types::Color as AcadColor;
|
||||||
use iced::advanced::layout::{self, Layout};
|
use iced::advanced::layout::{self, Layout};
|
||||||
use iced::advanced::widget::{self, Widget};
|
use iced::advanced::widget::{self, Widget};
|
||||||
use iced::advanced::{mouse, overlay, renderer, Shell};
|
use iced::advanced::{mouse, overlay, renderer, Shell};
|
||||||
use iced::widget::{button, column, container, row, scrollable, text};
|
use iced::widget::{button, column, container, row, text};
|
||||||
use iced::{Background, Border, Color, Element, Event, Length, Point, Rectangle, Renderer, Size, Theme, Vector};
|
use iced::{Background, Border, Color, Element, Event, Length, Point, Rectangle, Renderer, Size, Theme, Vector};
|
||||||
|
|
||||||
/// Which "logical" entries the colour list offers besides the standard ACI
|
/// Which "logical" entries the colour list offers besides the standard ACI
|
||||||
|
|
@ -21,17 +21,47 @@ pub struct ColorExtras {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode a colour as the ACI integer string the style editors store
|
/// Encode a colour as the ACI integer string the style editors store
|
||||||
/// (ByBlock=0, ByLayer=256, indexed 1-255; RGB has no ACI slot → ByLayer).
|
/// (ByBlock=0, ByLayer=256, indexed 1-255). True colours are mapped to the
|
||||||
|
/// closest ACI entry because these fields cannot store RGB values.
|
||||||
pub fn color_to_aci_string(c: AcadColor) -> String {
|
pub fn color_to_aci_string(c: AcadColor) -> String {
|
||||||
match c {
|
match c {
|
||||||
AcadColor::ByBlock => "0".to_string(),
|
AcadColor::ByBlock => "0".to_string(),
|
||||||
AcadColor::ByLayer => "256".to_string(),
|
AcadColor::ByLayer => "256".to_string(),
|
||||||
AcadColor::None => "257".to_string(),
|
AcadColor::None => "257".to_string(),
|
||||||
AcadColor::Index(i) => i.to_string(),
|
AcadColor::Index(i) => i.to_string(),
|
||||||
AcadColor::Rgb { .. } => "256".to_string(),
|
AcadColor::Rgb { r, g, b } => nearest_aci(r, g, b).to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an Iced colour chosen by `iced_aw::ColorPicker` into a DWG true
|
||||||
|
/// colour. ACI-only destinations map it to their closest indexed colour later.
|
||||||
|
pub fn iced_to_acad_color(color: Color) -> AcadColor {
|
||||||
|
let [r, g, b, _] = color.into_rgba8();
|
||||||
|
AcadColor::Rgb { r, g, b }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the closest AutoCAD Color Index for an RGB colour.
|
||||||
|
pub fn nearest_aci(r: u8, g: u8, b: u8) -> u8 {
|
||||||
|
let mut best = 7;
|
||||||
|
let mut best_distance = u32::MAX;
|
||||||
|
|
||||||
|
for index in 1..=255 {
|
||||||
|
let Some((ar, ag, ab)) = acadrust::types::aci_table::aci_to_rgb(index) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let dr = i32::from(r) - i32::from(ar);
|
||||||
|
let dg = i32::from(g) - i32::from(ag);
|
||||||
|
let db = i32::from(b) - i32::from(ab);
|
||||||
|
let distance = (dr * dr + dg * dg + db * db) as u32;
|
||||||
|
if distance < best_distance {
|
||||||
|
best = index;
|
||||||
|
best_distance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
best
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode an ACI integer string back into an `AcadColor`.
|
/// Decode an ACI integer string back into an `AcadColor`.
|
||||||
pub fn aci_string_to_color(s: &str) -> AcadColor {
|
pub fn aci_string_to_color(s: &str) -> AcadColor {
|
||||||
match s.trim().parse::<i16>().unwrap_or(256) {
|
match s.trim().parse::<i16>().unwrap_or(256) {
|
||||||
|
|
@ -197,80 +227,6 @@ pub fn color_list<'a>(
|
||||||
list.into()
|
list.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Full ACI palette as a standalone window body: ByLayer / ByBlock plus the
|
|
||||||
/// 256-colour grid. `on_pick` is called with the chosen colour.
|
|
||||||
pub fn color_grid_window(on_pick: impl Fn(AcadColor) -> Message) -> Element<'static, Message> {
|
|
||||||
let chip = |color: AcadColor, label: &'static str| -> Element<'static, Message> {
|
|
||||||
let (bg, _) = acad_color_display(color);
|
|
||||||
button(
|
|
||||||
row![swatch(bg), text(label).size(11)]
|
|
||||||
.spacing(5)
|
|
||||||
.align_y(iced::Center),
|
|
||||||
)
|
|
||||||
.on_press(on_pick(color))
|
|
||||||
.padding([3, 6])
|
|
||||||
.into()
|
|
||||||
};
|
|
||||||
|
|
||||||
const COLS: u16 = 16;
|
|
||||||
let mut grid = column![].spacing(2);
|
|
||||||
let mut idx: u16 = 1;
|
|
||||||
while idx <= 255 {
|
|
||||||
let mut r = row![].spacing(2);
|
|
||||||
for _ in 0..COLS {
|
|
||||||
if idx > 255 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let ci = idx as u8;
|
|
||||||
let (bg, _) = acad_color_display(AcadColor::Index(ci));
|
|
||||||
r = r.push(
|
|
||||||
button(text("").width(18).height(18))
|
|
||||||
.on_press(on_pick(AcadColor::Index(ci)))
|
|
||||||
.style(move |theme: &Theme, status| button::Style {
|
|
||||||
background: Some(Background::Color(bg)),
|
|
||||||
border: Border {
|
|
||||||
color: if matches!(status, button::Status::Hovered) {
|
|
||||||
theme.palette().primary.base.color
|
|
||||||
} else {
|
|
||||||
theme.palette().background.neutral.color
|
|
||||||
},
|
|
||||||
width: if matches!(status, button::Status::Hovered) {
|
|
||||||
1.5
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
},
|
|
||||||
radius: 1.0.into(),
|
|
||||||
},
|
|
||||||
text_color: theme.palette().background.base.text,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.padding(0),
|
|
||||||
);
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
grid = grid.push(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
container(
|
|
||||||
column![
|
|
||||||
text("Select Color").size(13),
|
|
||||||
row![chip(AcadColor::ByLayer, "ByLayer"), chip(AcadColor::ByBlock, "ByBlock")].spacing(6),
|
|
||||||
scrollable(grid).height(Length::Fit),
|
|
||||||
]
|
|
||||||
.spacing(8),
|
|
||||||
)
|
|
||||||
.style(|theme: &Theme| container::Style {
|
|
||||||
background: Some(Background::Color(
|
|
||||||
theme.palette().background.weak.color
|
|
||||||
)),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.padding(10)
|
|
||||||
.width(Length::Fit)
|
|
||||||
.height(Length::Fit)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render `base` inline with `popup` floating just below it — the shared
|
/// Render `base` inline with `popup` floating just below it — the shared
|
||||||
/// dropdown mechanic for the panel's custom dropdowns (colour picker, block
|
/// dropdown mechanic for the panel's custom dropdowns (colour picker, block
|
||||||
/// Name). Unlike iced's menu overlay it always opens downward.
|
/// Name). Unlike iced's menu overlay it always opens downward.
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,6 @@
|
||||||
/// Change this to scale the ribbon, layer manager rows, and property panel rows uniformly.
|
/// Change this to scale the ribbon, layer manager rows, and property panel rows uniformly.
|
||||||
pub const ROW_H: f32 = 26.0;
|
pub const ROW_H: f32 = 26.0;
|
||||||
|
|
||||||
/// Iced 0.15 separates pick-list construction from the selection callback.
|
|
||||||
/// Keep the application's established call shape while the rest of the UI
|
|
||||||
/// migrates independently.
|
|
||||||
pub fn pick_list<'a, T, L, V, Message>(
|
|
||||||
options: L,
|
|
||||||
selected: Option<V>,
|
|
||||||
on_select: impl Fn(T) -> Message + 'a,
|
|
||||||
) -> iced::widget::PickList<'a, T, L, V, Message>
|
|
||||||
where
|
|
||||||
T: PartialEq + Clone + ToString + 'a,
|
|
||||||
L: std::borrow::Borrow<[T]> + 'a,
|
|
||||||
V: std::borrow::Borrow<T> + 'a,
|
|
||||||
Message: Clone + 'a,
|
|
||||||
{
|
|
||||||
iced::widget::pick_list(selected, options, |value| value.to_string()).on_select(on_select)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Place `content` at fixed top-left coordinates inside a fill-sized layer.
|
|
||||||
/// Negative coordinates clamp to the layer edge.
|
|
||||||
pub fn pin_at<'a, Message: 'a>(
|
|
||||||
position: iced::Point,
|
|
||||||
content: impl Into<iced::Element<'a, Message>>,
|
|
||||||
) -> iced::Element<'a, Message> {
|
|
||||||
iced::widget::pin(content)
|
|
||||||
.position(iced::Point::new(position.x.max(0.0), position.y.max(0.0)))
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod color_select;
|
pub mod color_select;
|
||||||
pub mod command_line;
|
pub mod command_line;
|
||||||
pub mod icons;
|
pub mod icons;
|
||||||
|
|
|
||||||
|
|
@ -210,12 +210,6 @@ impl ModalOptions {
|
||||||
neutral_close: false,
|
neutral_close: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MOVABLE_FIXED: Self = Self {
|
|
||||||
movable: true,
|
|
||||||
resizable: false,
|
|
||||||
neutral_close: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const NOTICE: Self = Self {
|
pub const NOTICE: Self = Self {
|
||||||
movable: true,
|
movable: true,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
|
|
@ -223,6 +217,37 @@ impl ModalOptions {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dim and block the application beneath an overlay-owned dialog.
|
||||||
|
///
|
||||||
|
/// Some third-party overlays ignore pointer movement outside their interactive
|
||||||
|
/// controls. The shield owns the cursor and pointer presses across the window,
|
||||||
|
/// making such an overlay behave modally.
|
||||||
|
pub fn backdrop<'a>(
|
||||||
|
base: impl Into<Element<'a, Message>>,
|
||||||
|
on_close: Message,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
let shield = mouse_area(
|
||||||
|
container(Space::new())
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.style(|theme: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(
|
||||||
|
theme
|
||||||
|
.palette()
|
||||||
|
.background
|
||||||
|
.strongest
|
||||||
|
.color
|
||||||
|
.scale_alpha(0.55),
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_press(on_close)
|
||||||
|
.interaction(iced::mouse::Interaction::Idle);
|
||||||
|
|
||||||
|
stack![base.into(), opaque(shield)].into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Stack `content` over `base` behind a dimmed backdrop, framed with a title bar
|
/// Stack `content` over `base` behind a dimmed backdrop, framed with a title bar
|
||||||
/// (the ✕ close button at its right end). The backdrop only dims and blocks
|
/// (the ✕ close button at its right end). The backdrop only dims and blocks
|
||||||
/// clicks from reaching the view beneath — it does **not** dismiss the dialog;
|
/// clicks from reaching the view beneath — it does **not** dismiss the dialog;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ pub fn cycle_popup_overlay(
|
||||||
.style(container::bordered_box)
|
.style(container::bordered_box)
|
||||||
.width(Length::Fixed(150.0));
|
.width(Length::Fixed(150.0));
|
||||||
|
|
||||||
let positioned = crate::ui::pin_at(anchor, opaque(panel));
|
let positioned = iced::widget::pin(opaque(panel))
|
||||||
|
.position(iced::Point::new(anchor.x.max(0.0), anchor.y.max(0.0)));
|
||||||
|
|
||||||
mouse_area(positioned).on_press(Message::CycleCancel).into()
|
mouse_area(positioned).on_press(Message::CycleCancel).into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,7 +611,10 @@ impl PropertiesPanel {
|
||||||
// "More Colors…" opens the full palette window targeting the
|
// "More Colors…" opens the full palette window targeting the
|
||||||
// background colour — this used to just close the picker
|
// background colour — this used to just close the picker
|
||||||
// (#415).
|
// (#415).
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::PropertiesBg),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::PropertiesBg,
|
||||||
|
color,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return prop_row_widget(label, selector);
|
return prop_row_widget(label, selector);
|
||||||
}
|
}
|
||||||
|
|
@ -655,7 +658,10 @@ impl PropertiesPanel {
|
||||||
},
|
},
|
||||||
Message::PropColorChanged,
|
Message::PropColorChanged,
|
||||||
Message::PropColorPickerToggle,
|
Message::PropColorPickerToggle,
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::Properties),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::Properties,
|
||||||
|
color,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
prop_row_widget(label, selector)
|
prop_row_widget(label, selector)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1108,7 +1108,10 @@ impl Ribbon {
|
||||||
by_block: true,
|
by_block: true,
|
||||||
},
|
},
|
||||||
Message::RibbonColorChanged,
|
Message::RibbonColorChanged,
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::Ribbon),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::Ribbon,
|
||||||
|
self.active_color,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let panel = container(picker)
|
let panel = container(picker)
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,8 @@ pub fn view_window<'a>(
|
||||||
.unwrap_or_else(|| val.to_string());
|
.unwrap_or_else(|| val.to_string());
|
||||||
row![
|
row![
|
||||||
lbl(label),
|
lbl(label),
|
||||||
crate::ui::pick_list(labels, Some(cur), move |chosen| {
|
iced::widget::pick_list(Some(cur), labels, |value| value.to_string())
|
||||||
|
.on_select(move |chosen| {
|
||||||
let code = opts
|
let code = opts
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, l)| *l == chosen.as_str())
|
.find(|(_, l)| *l == chosen.as_str())
|
||||||
|
|
@ -287,7 +288,10 @@ pub fn view_window<'a>(
|
||||||
},
|
},
|
||||||
move |c| Message::DsEdit(f_sel.clone(), crate::ui::color_select::color_to_aci_string(c)),
|
move |c| Message::DsEdit(f_sel.clone(), crate::ui::color_select::color_to_aci_string(c)),
|
||||||
Message::DsColorMore(fld.clone()),
|
Message::DsColorMore(fld.clone()),
|
||||||
Message::OpenColorWindow(ColorPickTarget::DimStyle(fld.clone())),
|
Message::OpenColorWindow(
|
||||||
|
ColorPickTarget::DimStyle(fld.clone()),
|
||||||
|
cur,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
row![lbl(label), container(selector).width(150)]
|
row![lbl(label), container(selector).width(150)]
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
|
|
@ -304,9 +308,8 @@ pub fn view_window<'a>(
|
||||||
-> Element<'a, Message> {
|
-> Element<'a, Message> {
|
||||||
row![
|
row![
|
||||||
lbl(label),
|
lbl(label),
|
||||||
crate::ui::pick_list(options, Some(selected), move |value| {
|
iced::widget::pick_list(Some(selected), options, |value| value.to_string())
|
||||||
Message::DsSetHandle { field, value }
|
.on_select(move |value| Message::DsSetHandle { field, value })
|
||||||
})
|
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(150),
|
.width(150),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,10 @@ fn color_row<'a>(
|
||||||
value: crate::ui::color_select::color_to_aci_string(c),
|
value: crate::ui::color_select::color_to_aci_string(c),
|
||||||
},
|
},
|
||||||
Message::MLeaderColorMore(field),
|
Message::MLeaderColorMore(field),
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::MLeader(field)),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::MLeader(field),
|
||||||
|
cur,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
row![text(label).size(11).style(muted_style).width(150), selector]
|
row![text(label).size(11).style(muted_style).width(150), selector]
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
|
|
@ -142,9 +145,8 @@ fn enum_row<'a>(
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
row![
|
row![
|
||||||
text(label).size(11).style(muted_style).width(150),
|
text(label).size(11).style(muted_style).width(150),
|
||||||
crate::ui::pick_list(options, Some(selected), move |value| {
|
iced::widget::pick_list(Some(selected), options, |value| value.to_string())
|
||||||
Message::MLeaderStyleSetEnum { field, value }
|
.on_select(move |value| Message::MLeaderStyleSetEnum { field, value })
|
||||||
})
|
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(190),
|
.width(190),
|
||||||
]
|
]
|
||||||
|
|
@ -157,11 +159,12 @@ fn lineweight_row<'a>(selected: acadrust::types::LineWeight) -> Element<'a, Mess
|
||||||
let selected = crate::ui::properties::LwItem(selected);
|
let selected = crate::ui::properties::LwItem(selected);
|
||||||
row![
|
row![
|
||||||
text("Line weight:").size(11).style(muted_style).width(150),
|
text("Line weight:").size(11).style(muted_style).width(150),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
crate::ui::properties::lw_options(),
|
|
||||||
Some(selected),
|
Some(selected),
|
||||||
|item| Message::MLeaderStyleLineWeightChanged(item.0)
|
crate::ui::properties::lw_options(),
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(|item| Message::MLeaderStyleLineWeightChanged(item.0))
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(190),
|
.width(190),
|
||||||
]
|
]
|
||||||
|
|
@ -199,9 +202,8 @@ fn handle_row<'a>(
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
row![
|
row![
|
||||||
text(label).size(11).style(muted_style).width(150),
|
text(label).size(11).style(muted_style).width(150),
|
||||||
crate::ui::pick_list(options, Some(selected), move |value| {
|
iced::widget::pick_list(Some(selected), options, |value| value.to_string())
|
||||||
Message::MLeaderStyleSetHandle { field, value }
|
.on_select(move |value| Message::MLeaderStyleSetHandle { field, value })
|
||||||
})
|
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(190),
|
.width(190),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,10 @@ pub fn view_window<'a>(
|
||||||
value: crate::ui::color_select::color_to_aci_string(c),
|
value: crate::ui::color_select::color_to_aci_string(c),
|
||||||
},
|
},
|
||||||
Message::TableColorMore(row, field),
|
Message::TableColorMore(row, field),
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::Table(row, field)),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::Table(row, field),
|
||||||
|
cur,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
row![text(label).size(11).style(muted_style).width(150), selector]
|
row![text(label).size(11).style(muted_style).width(150), selector]
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
|
|
@ -141,7 +144,8 @@ pub fn view_window<'a>(
|
||||||
.push(
|
.push(
|
||||||
row![
|
row![
|
||||||
text(" Alignment:").size(11).style(muted_style).width(150),
|
text(" Alignment:").size(11).style(muted_style).width(150),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
|
Some(format!("{:?}", rs.alignment)),
|
||||||
[
|
[
|
||||||
"TopLeft",
|
"TopLeft",
|
||||||
"TopCenter",
|
"TopCenter",
|
||||||
|
|
@ -156,9 +160,9 @@ pub fn view_window<'a>(
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
Some(format!("{:?}", rs.alignment)),
|
|value| value.to_string(),
|
||||||
move |value| Message::TableStyleCellSetAlign { row, value },
|
|
||||||
)
|
)
|
||||||
|
.on_select(move |value| Message::TableStyleCellSetAlign { row, value })
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(140),
|
.width(140),
|
||||||
]
|
]
|
||||||
|
|
@ -194,18 +198,19 @@ pub fn view_window<'a>(
|
||||||
col = col.push(
|
col = col.push(
|
||||||
row![
|
row![
|
||||||
text(format!(" {bname}")).size(11).style(muted_style).width(28),
|
text(format!(" {bname}")).size(11).style(muted_style).width(28),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
|
Some(format!("{:?}", bd.border_type)),
|
||||||
["Single", "Double"]
|
["Single", "Double"]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
Some(format!("{:?}", bd.border_type)),
|
|value| value.to_string(),
|
||||||
move |value| Message::TableStyleBorderSetType {
|
)
|
||||||
|
.on_select(move |value| Message::TableStyleBorderSetType {
|
||||||
cell: row,
|
cell: row,
|
||||||
border: bu,
|
border: bu,
|
||||||
value
|
value
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.text_size(10)
|
.text_size(10)
|
||||||
.width(74),
|
.width(74),
|
||||||
text_input("wt", &border_lw[r][b])
|
text_input("wt", &border_lw[r][b])
|
||||||
|
|
@ -274,14 +279,15 @@ pub fn view_window<'a>(
|
||||||
.align_y(iced::Center),
|
.align_y(iced::Center),
|
||||||
row![
|
row![
|
||||||
text("Flow direction:").size(11).style(muted_style).width(160),
|
text("Flow direction:").size(11).style(muted_style).width(160),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
|
Some(format!("{:?}", s.flow_direction)),
|
||||||
["Down", "Up"]
|
["Down", "Up"]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
Some(format!("{:?}", s.flow_direction)),
|
|value| value.to_string(),
|
||||||
Message::TableStyleSetFlow,
|
|
||||||
)
|
)
|
||||||
|
.on_select(Message::TableStyleSetFlow)
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.width(100),
|
.width(100),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,8 @@ fn pick_field<'a>(
|
||||||
on_select: impl Fn(String) -> Message + 'a,
|
on_select: impl Fn(String) -> Message + 'a,
|
||||||
width: Length,
|
width: Length,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let pl = crate::ui::pick_list(options, selected, on_select)
|
let pl = iced::widget::pick_list(selected, options, |value| value.to_string())
|
||||||
|
.on_select(on_select)
|
||||||
.text_size(13)
|
.text_size(13)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(width);
|
.width(width);
|
||||||
|
|
@ -475,9 +476,8 @@ fn properties_tab<'a>(
|
||||||
|
|
||||||
let lw_opts = lw_options();
|
let lw_opts = lw_options();
|
||||||
let lw_sel = LwItem(r.line_weight);
|
let lw_sel = LwItem(r.line_weight);
|
||||||
let lw = crate::ui::pick_list(lw_opts, Some(lw_sel), |it: LwItem| {
|
let lw = iced::widget::pick_list(Some(lw_sel), lw_opts, |value| value.to_string())
|
||||||
Message::AttrEditorLineweight(it.0)
|
.on_select(|it: LwItem| Message::AttrEditorLineweight(it.0))
|
||||||
})
|
|
||||||
.text_size(13)
|
.text_size(13)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(width);
|
.width(width);
|
||||||
|
|
|
||||||
|
|
@ -413,7 +413,10 @@ fn editor_layer_row<'a>(
|
||||||
},
|
},
|
||||||
move |color| Message::LayerStateEditorLayerColor(index, color),
|
move |color| Message::LayerStateEditorLayerColor(index, color),
|
||||||
Message::LayerStateEditorLayerColorToggle(index),
|
Message::LayerStateEditorLayerColorToggle(index),
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::LayerState(index)),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::LayerState(index),
|
||||||
|
layer.color,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
container(
|
container(
|
||||||
|
|
@ -432,13 +435,21 @@ fn editor_layer_row<'a>(
|
||||||
54.0
|
54.0
|
||||||
),
|
),
|
||||||
container(color).width(Length::Fixed(135.0)),
|
container(color).width(Length::Fixed(135.0)),
|
||||||
crate::ui::pick_list(linetypes, current_linetype, move |value| {
|
iced::widget::pick_list(
|
||||||
Message::LayerStateEditorLayerLinetype(index, value)
|
current_linetype,
|
||||||
})
|
linetypes,
|
||||||
|
|value| value.to_string(),
|
||||||
|
)
|
||||||
|
.on_select(move |value| Message::LayerStateEditorLayerLinetype(index, value))
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.padding([3, 5])
|
.padding([3, 5])
|
||||||
.width(Length::Fixed(150.0)),
|
.width(Length::Fixed(150.0)),
|
||||||
crate::ui::pick_list(lw_options(), current_lineweight, move |item: LwItem| {
|
iced::widget::pick_list(
|
||||||
|
current_lineweight,
|
||||||
|
lw_options(),
|
||||||
|
|value| value.to_string(),
|
||||||
|
)
|
||||||
|
.on_select(move |item: LwItem| {
|
||||||
Message::LayerStateEditorLayerLineweight(index, item.0)
|
Message::LayerStateEditorLayerLineweight(index, item.0)
|
||||||
})
|
})
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
|
|
@ -449,11 +460,12 @@ fn editor_layer_row<'a>(
|
||||||
.size(11)
|
.size(11)
|
||||||
.padding([3, 5])
|
.padding([3, 5])
|
||||||
.width(Length::Fixed(135.0)),
|
.width(Length::Fixed(135.0)),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
transparency_options(layer.transparency),
|
|
||||||
Some(TransparencyItem(layer.transparency)),
|
Some(TransparencyItem(layer.transparency)),
|
||||||
move |item| Message::LayerStateEditorLayerTransparency(index, item.0),
|
transparency_options(layer.transparency),
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(move |item| Message::LayerStateEditorLayerTransparency(index, item.0))
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.padding([3, 5])
|
.padding([3, 5])
|
||||||
.width(Length::Fixed(105.0)),
|
.width(Length::Fixed(105.0)),
|
||||||
|
|
@ -543,11 +555,12 @@ pub fn view_editor<'a>(
|
||||||
.style(muted),
|
.style(muted),
|
||||||
Space::new().width(sizing.width),
|
Space::new().width(sizing.width),
|
||||||
text("Current layer").size(10).style(muted),
|
text("Current layer").size(10).style(muted),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
layer_names,
|
|
||||||
Some(state.current_layer.clone()),
|
Some(state.current_layer.clone()),
|
||||||
Message::LayerStateEditorCurrentLayer,
|
layer_names,
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(Message::LayerStateEditorCurrentLayer)
|
||||||
.text_size(11)
|
.text_size(11)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(Length::Fixed(180.0)),
|
.width(Length::Fixed(180.0)),
|
||||||
|
|
|
||||||
|
|
@ -780,7 +780,10 @@ fn layer_row<'a>(
|
||||||
_ => Message::LayerColorSet(7),
|
_ => Message::LayerColorSet(7),
|
||||||
},
|
},
|
||||||
Message::LayerColorPickerToggle(index),
|
Message::LayerColorPickerToggle(index),
|
||||||
Message::OpenColorWindow(crate::app::ColorPickTarget::Layer(index)),
|
Message::OpenColorWindow(
|
||||||
|
crate::app::ColorPickTarget::Layer(index),
|
||||||
|
acadrust::types::Color::Index(aci),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.width(Length::Fixed(COL_COLOR))
|
.width(Length::Fixed(COL_COLOR))
|
||||||
.into();
|
.into();
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,12 @@ pub fn view_window<'a>(
|
||||||
Space::new().height(10),
|
Space::new().height(10),
|
||||||
row![
|
row![
|
||||||
text("Default save format:").size(12).width(150),
|
text("Default save format:").size(12).width(150),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
crate::io::SAVE_FORMAT_OPTIONS,
|
|
||||||
selected_format,
|
selected_format,
|
||||||
|format: &str| Message::DefaultSaveFormatChanged(format.to_string())
|
crate::io::SAVE_FORMAT_OPTIONS,
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(|format: &str| Message::DefaultSaveFormatChanged(format.to_string()))
|
||||||
.width(sizing.width),
|
.width(sizing.width),
|
||||||
]
|
]
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
|
|
@ -90,11 +91,12 @@ pub fn view_window<'a>(
|
||||||
Space::new().height(10),
|
Space::new().height(10),
|
||||||
row![
|
row![
|
||||||
text("Iced theme:").size(12).width(150),
|
text("Iced theme:").size(12).width(150),
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
theme_options,
|
|
||||||
selected_theme,
|
selected_theme,
|
||||||
Message::OptionsThemeChanged,
|
theme_options,
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(Message::OptionsThemeChanged)
|
||||||
.width(sizing.width),
|
.width(sizing.width),
|
||||||
]
|
]
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,8 @@ fn drop_row<'a>(
|
||||||
ctor: fn(String) -> PlotDlgMsg,
|
ctor: fn(String) -> PlotDlgMsg,
|
||||||
width: Length,
|
width: Length,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let pl = crate::ui::pick_list(options, selected, move |s| Message::PlotDlg(ctor(s)))
|
let pl = iced::widget::pick_list(selected, options, |value| value.to_string())
|
||||||
|
.on_select(move |s| Message::PlotDlg(ctor(s)))
|
||||||
.text_size(12)
|
.text_size(12)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(width);
|
.width(width);
|
||||||
|
|
@ -536,11 +537,12 @@ pub fn view_window(
|
||||||
section_label("Plot area"),
|
section_label("Plot area"),
|
||||||
row![
|
row![
|
||||||
container(
|
container(
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
strs(&["Layout", "Extents", "Display", "Window"]),
|
|
||||||
Some(s.area.clone()),
|
Some(s.area.clone()),
|
||||||
move |v| Message::PlotDlg(PlotDlgMsg::Area(v)),
|
strs(&["Layout", "Extents", "Display", "Window"]),
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(move |v| Message::PlotDlg(PlotDlgMsg::Area(v)))
|
||||||
.text_size(12)
|
.text_size(12)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(width)
|
.width(width)
|
||||||
|
|
@ -608,11 +610,12 @@ pub fn view_window(
|
||||||
section_label("Quality"),
|
section_label("Quality"),
|
||||||
row![
|
row![
|
||||||
container(
|
container(
|
||||||
crate::ui::pick_list(
|
iced::widget::pick_list(
|
||||||
strs(&["Draft", "Normal", "High", "Maximum"]),
|
|
||||||
Some(s.quality.clone()),
|
Some(s.quality.clone()),
|
||||||
move |v| Message::PlotDlg(PlotDlgMsg::Quality(v)),
|
strs(&["Draft", "Normal", "High", "Maximum"]),
|
||||||
|
|value| value.to_string(),
|
||||||
)
|
)
|
||||||
|
.on_select(move |v| Message::PlotDlg(PlotDlgMsg::Quality(v)))
|
||||||
.text_size(12)
|
.text_size(12)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(width)
|
.width(width)
|
||||||
|
|
|
||||||
|
|
@ -325,9 +325,8 @@ fn install_controls<'a>(
|
||||||
text("no releases").size(11).style(muted_style).into()
|
text("no releases").size(11).style(muted_style).into()
|
||||||
} else {
|
} else {
|
||||||
let r = repo_s.clone();
|
let r = repo_s.clone();
|
||||||
crate::ui::pick_list(tags, selected, move |tag| {
|
iced::widget::pick_list(selected, tags, |value| value.to_string())
|
||||||
Message::PluginReleaseSelect(r.clone(), tag)
|
.on_select(move |tag| Message::PluginReleaseSelect(r.clone(), tag))
|
||||||
})
|
|
||||||
.text_size(12)
|
.text_size(12)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue