Merge main into codex/graphic-attributes-cleanup

Resolve conflicts from main's rustfmt pass by taking main's versions of
command.rs, style.rs, view/mod.rs, scene/mod.rs, ui/window/mod.rs and
ui/properties.rs and re-applying this branch's Graphic Attributes changes.
Add the Graphic Attributes strings to the new lo-LA catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1QnME6WJbD5iGFae1eeCb
This commit is contained in:
Claude committed 2026-10-11 19:09:37 +00:00
commit 44f8f76ab3
596 files changed
+69069 -34120

No files matched your search

+57 -56
View File
@@ -15,8 +15,8 @@ use iced::advanced::layout::{self, Layout};
use iced::advanced::widget::{self, Widget};
use iced::advanced::{mouse, overlay, renderer, Renderer as _, Shell};
use iced::{
Background, Border, Element, Event, Length, Point, Rectangle, Renderer, Shadow, Size,
Theme, Vector,
Background, Border, Element, Event, Length, Point, Rectangle, Renderer, Shadow, Size, Theme,
Vector,
};
use crate::app::Message;
@@ -59,9 +59,7 @@ const MAX_PANEL_SQUEEZE: f32 = 8.0;
/// How the ribbon tool panels are sized. `Auto` adapts to the window width (the
/// step-by-step degradation); the others pin every panel to one density so the
/// user can override the automatic choice. The selection is persisted.
#[derive(
Clone, Copy, PartialEq, Eq, Debug, Default, serde::Serialize, serde::Deserialize,
)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, serde::Serialize, serde::Deserialize)]
pub enum CollapseMode {
/// Size panels to the window: degrade from the right as space runs out.
#[default]
@@ -93,7 +91,6 @@ impl CollapseMode {
}
}
/// The degradation level every panel is pinned to, or `None` for `Auto`.
fn forced_level(self) -> Option<Level> {
match self {
@@ -103,7 +100,6 @@ impl CollapseMode {
CollapseMode::Collapsed => Some(Level::Collapsed),
}
}
}
impl std::fmt::Display for CollapseMode {
@@ -130,11 +126,7 @@ pub(crate) struct Widths {
/// panel at a time: first Full -> Compact, then Compact -> Collapsed, each phase
/// only while the row still overflows; if even the all-collapsed row overflows,
/// every collapsed panel drops to Tight at once. Forced modes pin everything.
pub(crate) fn decide_levels(
mode: CollapseMode,
widths: &[Widths],
max_w: f32,
) -> Vec<Level> {
pub(crate) fn decide_levels(mode: CollapseMode, widths: &[Widths], max_w: f32) -> Vec<Level> {
if let Some(level) = mode.forced_level() {
return vec![level; widths.len()];
}
@@ -144,9 +136,8 @@ pub(crate) fn decide_levels(
Level::Collapsed => widths[i].button,
Level::Tight => widths[i].tight,
};
let total = |levels: &[Level]| -> f32 {
(0..levels.len()).map(|i| width_of(levels[i], i)).sum()
};
let total =
|levels: &[Level]| -> f32 { (0..levels.len()).map(|i| width_of(levels[i], i)).sum() };
let mut levels = vec![Level::Full; widths.len()];
for degraded in [Level::Compact, Level::Collapsed] {
for i in (0..widths.len()).rev() {
@@ -264,17 +255,33 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
let auto = self.mode == CollapseMode::Auto;
if auto {
for i in 0..n {
let f = self.panels[i].elements[Level::Full as usize].as_widget_mut()
let f = self.panels[i].elements[Level::Full as usize]
.as_widget_mut()
.layout(&mut tree.children[slot(i, Level::Full)], renderer, &natural)
.size();
let c = self.panels[i].elements[Level::Compact as usize].as_widget_mut()
.layout(&mut tree.children[slot(i, Level::Compact)], renderer, &natural)
let c = self.panels[i].elements[Level::Compact as usize]
.as_widget_mut()
.layout(
&mut tree.children[slot(i, Level::Compact)],
renderer,
&natural,
)
.size();
let b = self.panels[i].elements[Level::Collapsed as usize].as_widget_mut()
.layout(&mut tree.children[slot(i, Level::Collapsed)], renderer, &natural)
let b = self.panels[i].elements[Level::Collapsed as usize]
.as_widget_mut()
.layout(
&mut tree.children[slot(i, Level::Collapsed)],
renderer,
&natural,
)
.size();
let t = self.panels[i].elements[Level::Tight as usize].as_widget_mut()
.layout(&mut tree.children[slot(i, Level::Tight)], renderer, &natural)
let t = self.panels[i].elements[Level::Tight as usize]
.as_widget_mut()
.layout(
&mut tree.children[slot(i, Level::Tight)],
renderer,
&natural,
)
.size();
widths.push(Widths {
full: f.width,
@@ -294,10 +301,7 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
// The row is "tight" once any panel has dropped to its small icon — the
// last, most cramped state. The tab bar hides its mode selector then.
if let Some(out) = &self.tight_out {
out.store(
levels.iter().any(|&l| l == Level::Tight),
Ordering::Relaxed,
);
out.store(levels.iter().any(|&l| l == Level::Tight), Ordering::Relaxed);
}
*self.levels.borrow_mut() = levels.clone();
@@ -357,10 +361,7 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
.fold(0.0f32, f32::max)
}
} else {
placed
.iter()
.map(|(_, _, h)| *h)
.fold(self.row_h, f32::max)
placed.iter().map(|(_, _, h)| *h).fold(self.row_h, f32::max)
};
let children: Vec<layout::Node> = placed
.into_iter()
@@ -522,8 +523,7 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
{
let child_layout = layout.children().nth(p).unwrap();
let b = child_layout.bounds();
let anchor =
Point::new(b.x + translation.x, b.y + b.height + translation.y);
let anchor = Point::new(b.x + translation.x, b.y + b.height + translation.y);
// The flyout is the panel's own `full` rendering (the row
// never shows slot 0 while the panel is button-form, so it
// is free to double as the overlay content).
@@ -543,8 +543,7 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
// Split borrows so each shown child and its tree slot are disjoint.
let mut overlays = Vec::new();
let mut tree_rest = tree.children.as_mut_slice();
for ((i, panel), child_layout) in
self.panels.iter_mut().enumerate().zip(layout.children())
for ((i, panel), child_layout) in self.panels.iter_mut().enumerate().zip(layout.children())
{
if tree_rest.len() < SLOTS {
break;
@@ -683,9 +682,13 @@ impl overlay::Overlay<Message, Theme, Renderer> for FlyoutOverlay<'_, '_> {
renderer: &Renderer,
) -> mouse::Interaction {
let child = layout.children().next().unwrap();
self.content
.as_widget()
.mouse_interaction(self.tree, child, cursor, &child.bounds(), renderer)
self.content.as_widget().mouse_interaction(
self.tree,
child,
cursor,
&child.bounds(),
renderer,
)
}
}
@@ -734,10 +737,7 @@ mod tests {
/// compact first, and stops as soon as the row fits.
#[test]
fn auto_degrades_from_the_right_one_panel_at_a_time() {
let widths = [
w(50.0, 40.0, 30.0, 20.0),
w(200.0, 100.0, 60.0, 30.0),
];
let widths = [w(50.0, 40.0, 30.0, 20.0), w(200.0, 100.0, 60.0, 30.0)];
// Full row = 250 > 240; rightmost compact => 50 + 100 = 150 <= 240.
assert_eq!(
decide_levels(CollapseMode::Auto, &widths, 240.0),
@@ -800,10 +800,7 @@ mod tests {
fn no_panels_stays_empty() {
assert_eq!(decide_levels(CollapseMode::Auto, &[], 1e9), vec![]);
assert_eq!(decide_levels(CollapseMode::Full, &[], 1e9), vec![]);
assert_eq!(
decide_levels(CollapseMode::Collapsed, &[], 1e9),
vec![]
);
assert_eq!(decide_levels(CollapseMode::Collapsed, &[], 1e9), vec![]);
}
/// A single panel walked the whole ladder as the width shrinks: FULL,
@@ -910,9 +907,8 @@ mod tests {
2 => widths[i].button,
_ => widths[i].tight,
};
let total = |levels: &[u8]| -> f32 {
(0..levels.len()).map(|i| width_of(levels[i], i)).sum()
};
let total =
|levels: &[u8]| -> f32 { (0..levels.len()).map(|i| width_of(levels[i], i)).sum() };
let mut levels = vec![0u8; widths.len()];
for degraded in [1u8, 2u8] {
for i in (0..widths.len()).rev() {
@@ -923,10 +919,7 @@ mod tests {
}
}
if total(&levels) > max_w {
levels
.iter_mut()
.filter(|l| **l == 2)
.for_each(|l| *l = 3);
levels.iter_mut().filter(|l| **l == 2).for_each(|l| *l = 3);
}
levels
}
@@ -936,13 +929,21 @@ mod tests {
for _ in 0..40 {
let mut widths = Vec::with_capacity(n);
for _ in 0..n {
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
rng = rng
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let full = (rng % 2000) as f32 / 10.0;
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
rng = rng
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let compact = (rng % (full as u64 * 10 + 1)) as f32 / 10.0;
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
rng = rng
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let button = (rng % (compact as u64 * 10 + 1)) as f32 / 10.0;
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
rng = rng
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let tight = (rng % (button as u64 * 10 + 1)) as f32 / 10.0;
widths.push(Widths {
full,
+21 -29
View File
@@ -134,7 +134,9 @@ fn placeholder_swatch<'a>() -> Element<'a, Message> {
.style(|theme: &Theme| {
let palette = theme.palette();
container::Style {
background: Some(Background::Color(palette.background.weak.color.scale_alpha(0.5))),
background: Some(Background::Color(
palette.background.weak.color.scale_alpha(0.5),
)),
border: Border {
color: palette.background.neutral.color.scale_alpha(0.5),
width: 1.0,
@@ -147,16 +149,12 @@ fn placeholder_swatch<'a>() -> Element<'a, Message> {
}
fn divider<'a>() -> Element<'a, Message> {
container(
Space::new()
.width(Length::Fill)
.height(Length::Fixed(1.0)),
)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(theme.palette().background.neutral.color)),
..Default::default()
})
.into()
container(Space::new().width(Length::Fill).height(Length::Fixed(1.0)))
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(theme.palette().background.neutral.color)),
..Default::default()
})
.into()
}
fn logical_row<'a>(
@@ -190,12 +188,9 @@ fn logical_row<'a>(
});
button(
row![
swatch,
text(t!(label)).size(11),
]
.spacing(8)
.align_y(iced::Center),
row![swatch, text(t!(label)).size(11),]
.spacing(8)
.align_y(iced::Center),
)
.on_press(Message::RibbonColorChanged(color))
.style(popup_row_style)
@@ -205,18 +200,15 @@ fn logical_row<'a>(
}
fn more_colors_row<'a>(active_color: AcadColor) -> Element<'a, Message> {
button(
row![text(t!("Select Color...")).size(11)]
.align_y(iced::Center),
)
.on_press(Message::OpenColorWindow(
crate::app::ColorPickTarget::Ribbon,
active_color,
))
.style(popup_row_style)
.padding([4, 4])
.width(Length::Fill)
.into()
button(row![text(t!("Select Color...")).size(11)].align_y(iced::Center))
.on_press(Message::OpenColorWindow(
crate::app::ColorPickTarget::Ribbon,
active_color,
))
.style(popup_row_style)
.padding([4, 4])
.width(Length::Fill)
.into()
}
/// Builds the complete 5-section color dropdown panel element.
+20 -4
View File
@@ -42,7 +42,11 @@ pub fn pdf_underlay_tools(kind: codec::entities::UnderlayType) -> Vec<ToolDef> {
"Enable\nSnap",
include_bytes!("../../../assets/icons/snap_underlays.svg"),
),
tool("EXTERNALREFERENCES", "External\nReferences", crate::ui::icons::FOLDER_OPEN),
tool(
"EXTERNALREFERENCES",
"External\nReferences",
crate::ui::icons::FOLDER_OPEN,
),
tool(
"ULAYERS",
"Edit\nLayers",
@@ -73,7 +77,11 @@ pub fn point_cloud_tools() -> Vec<ToolDef> {
),
tool("_PCCROPINVERT", "Invert\nCropping", CLIP),
tool("_PCUNCROP", "Remove All\nCropping", UNCLIP),
tool("EXTERNALREFERENCES", "External\nReferences", crate::ui::icons::FOLDER_OPEN),
tool(
"EXTERNALREFERENCES",
"External\nReferences",
crate::ui::icons::FOLDER_OPEN,
),
]
}
@@ -85,10 +93,18 @@ pub fn xref_tools() -> Vec<ToolDef> {
"Edit Reference\nIn-Place",
include_bytes!("../../../assets/icons/edit_block.svg"),
),
tool("_XREFOPEN", "Open\nReference", crate::ui::icons::FOLDER_OPEN),
tool(
"_XREFOPEN",
"Open\nReference",
crate::ui::icons::FOLDER_OPEN,
),
tool("_XREFCLIP", "Create Clipping\nBoundary", CLIP),
tool("_XREFUNCLIP", "Remove\nClipping", UNCLIP),
tool("EXTERNALREFERENCES", "External\nReferences", crate::ui::icons::FOLDER_OPEN),
tool(
"EXTERNALREFERENCES",
"External\nReferences",
crate::ui::icons::FOLDER_OPEN,
),
]
}
+54 -11
View File
@@ -125,21 +125,50 @@ const REFERENCE_TOOLS: &[Tool] = &[Tool {
pub(super) const REFERENCE_PANEL_ID: &str = "reference_extension";
const fn icon_tool(command: &'static str, label: &'static str, icon: &'static [u8]) -> Tool {
Tool { command, label, icon, options: &[] }
Tool {
command,
label,
icon,
options: &[],
}
}
/// Home > Block slide-out.
const HOME_BLOCK_TOOLS: &[Tool] = &[
icon_tool("ATTDEF", "Define Attributes", include_bytes!("../../../assets/icons/attdef.svg")),
icon_tool("ATTMAN", "Manage Attributes", include_bytes!("../../../assets/icons/attman.svg")),
icon_tool("ATTSYNC", "Synchronize", include_bytes!("../../../assets/icons/attsync.svg")),
icon_tool("BASE", "Set Base Point", include_bytes!("../../../assets/icons/base_point.svg")),
icon_tool(
"ATTDEF",
"Define Attributes",
include_bytes!("../../../assets/icons/attdef.svg"),
),
icon_tool(
"ATTMAN",
"Manage Attributes",
include_bytes!("../../../assets/icons/attman.svg"),
),
icon_tool(
"ATTSYNC",
"Synchronize",
include_bytes!("../../../assets/icons/attsync.svg"),
),
icon_tool(
"BASE",
"Set Base Point",
include_bytes!("../../../assets/icons/base_point.svg"),
),
];
/// Insert > Block Definition slide-out.
const BLOCK_DEFINITION_TOOLS: &[Tool] = &[
icon_tool("BASE", "Set Base Point", include_bytes!("../../../assets/icons/base_point.svg")),
icon_tool("ATTSYNC", "Synchronize", include_bytes!("../../../assets/icons/attsync.svg")),
icon_tool(
"BASE",
"Set Base Point",
include_bytes!("../../../assets/icons/base_point.svg"),
),
icon_tool(
"ATTSYNC",
"Synchronize",
include_bytes!("../../../assets/icons/attsync.svg"),
),
];
const PANELS: &[Panel] = &[
@@ -213,7 +242,9 @@ fn panel_for_group(title: &str, group_ids: &[&'static str]) -> Option<Panel> {
PANELS.iter().copied().find(|panel| {
panel.title == title
&& !panel.tools.is_empty()
&& panel.anchor.is_none_or(|anchor| group_ids.contains(&anchor))
&& panel
.anchor
.is_none_or(|anchor| group_ids.contains(&anchor))
})
}
@@ -252,7 +283,11 @@ pub(super) fn group_title<'a>(
let expanded = open.as_deref().and_then(parent_panel) == Some(panel.id);
// A command running from the closed slide-out lights its title, the way a
// dropdown lights while one of its items runs.
let lit = expanded || panel.tools.iter().any(|tool| tool_is_active(tool, active_tool));
let lit = expanded
|| panel
.tools
.iter()
.any(|tool| tool_is_active(tool, active_tool));
let arrow = if expanded {
icons::themed_arrow_up(GROUP_TITLE_ARROW_SIZE)
} else {
@@ -294,7 +329,11 @@ pub(super) fn group_title<'a>(
})
.style(move |theme: &Theme, status| tool_btn_style(theme, false, status))
.padding([0, 3]),
make_tip(format!("{}\n{} EXTERNALREFERENCES", t!("External References"), t!("Command:"))),
make_tip(format!(
"{}\n{} EXTERNALREFERENCES",
t!("External References"),
t!("Command:")
)),
tooltip::Position::Bottom,
)
.delay(Duration::from_millis(400))
@@ -377,7 +416,11 @@ fn reference_overlay<'a>(ribbon: &Ribbon, panel: Panel, win: (f32, f32)) -> Elem
let fading = row![
tooltip(
switch,
make_tip(format!("{}\n{} XDWGFADECTL", t!("Xref fading"), t!("Command:"))),
make_tip(format!(
"{}\n{} XDWGFADECTL",
t!("Xref fading"),
t!("Command:")
)),
tooltip::Position::Bottom
)
.delay(Duration::from_millis(400))
+123 -175
View File
@@ -20,21 +20,21 @@ use crate::modules::{CadModule, IconKind, RibbonGroup, RibbonItem};
use crate::plugin::all_ribbon_modules;
use crate::ui::properties::{linetype_display_name, lw_options, LinetypeItem};
pub(crate) mod widgets;
mod draw_panel;
pub(crate) mod widgets;
pub(crate) use draw_panel::tools as panel_tools;
mod modify_panel;
mod color_dropdown;
mod context_tools;
mod modify_panel;
pub use context_tools::{pdf_underlay_tools, point_cloud_tools, xref_tools, UnderlayContext};
use widgets::{StyleContext, *};
pub(crate) use widgets::{REDO_HISTORY_ID, UNDO_HISTORY_ID};
mod collapse;
mod locate;
use collapse::{CollapsePanels, Panel};
pub use collapse::CollapseMode;
use crate::ui::wrap_bar::{PosReport, WrapBar, WrapFlow};
use crate::t;
use crate::ui::wrap_bar::{PosReport, WrapBar, WrapFlow};
pub use collapse::CollapseMode;
use collapse::{CollapsePanels, Panel};
pub(crate) fn tooltip_content(text: String) -> Element<'static, Message> {
widgets::make_tip(text)
@@ -75,7 +75,6 @@ pub struct Ribbon {
/// shows this tool on its button, defaulting to the panel's first tool.
last_panel_tool: HashMap<&'static str, &'static str>,
last_cmd: HashMap<&'static str, &'static str>,
pub layer_names: Vec<String>,
pub active_layer: String,
pub layer_infos: Vec<LayerInfo>,
/// Live filter for the layer dropdown's search box (#343). Cleared on
@@ -192,7 +191,6 @@ impl Ribbon {
last_panel_tool: HashMap::default(),
last_cmd: HashMap::default(),
// Empty until a document is open — populated by sync_ribbon_layers.
layer_names: vec![],
active_layer: String::new(),
layer_infos: vec![],
layer_filter: String::new(),
@@ -288,7 +286,6 @@ impl Ribbon {
pub fn set_layers(&mut self, infos: Vec<LayerInfo>, active: &str) {
self.active_layer = active.to_string();
self.layer_names = infos.iter().map(|l| l.name.clone()).collect();
self.layer_infos = infos;
}
@@ -561,36 +558,42 @@ impl Ribbon {
let is_contextual = module.id() == "layout";
let holds_tool = holding_tab == Some(i);
let btn = container(
button(text(crate::i18n::ribbon_module_title(module.id(), module.title())).size(12))
.on_press(Message::RibbonSelectTab(i))
.style(move |theme: &Theme, status| {
let palette = theme.palette();
let accent = if is_contextual {
palette.warning.base
} else {
palette.primary.base
};
let pair = match (is_active, status) {
(true, _) => palette.background.weakest,
(false, _) if holds_tool => palette.primary.weak,
(false, button::Status::Hovered) => {
if is_contextual {
palette.warning.weak
} else {
palette.background.weak
}
button(
text(crate::i18n::ribbon_module_title(
module.id(),
module.title(),
))
.size(12),
)
.on_press(Message::RibbonSelectTab(i))
.style(move |theme: &Theme, status| {
let palette = theme.palette();
let accent = if is_contextual {
palette.warning.base
} else {
palette.primary.base
};
let pair = match (is_active, status) {
(true, _) => palette.background.weakest,
(false, _) if holds_tool => palette.primary.weak,
(false, button::Status::Hovered) => {
if is_contextual {
palette.warning.weak
} else {
palette.background.weak
}
_ => palette.background.base,
};
button::Style {
}
_ => palette.background.base,
};
button::Style {
background: (is_active
|| holds_tool
|| matches!(status, button::Status::Hovered))
.then_some(Background::Color(if holds_tool && !is_active {
pair.color.scale_alpha(0.35)
} else {
pair.color
})),
.then_some(Background::Color(if holds_tool && !is_active {
pair.color.scale_alpha(0.35)
} else {
pair.color
})),
text_color: if is_active {
pair.text
} else if holds_tool {
@@ -611,9 +614,9 @@ impl Ribbon {
},
shadow: iced::Shadow::default(),
snap: false,
}
})
.padding([5, 14]),
}
})
.padding([5, 14]),
)
.style(move |theme: &Theme| container::Style {
border: Border {
@@ -636,7 +639,6 @@ impl Ribbon {
},
);
// Tabs may squeeze their gaps to fit before wrapping: from the normal 6px
// down to -12px on a narrow (e.g. phone) tab row, tucking neighbours into
// each other's 14px side padding without overlapping the labels. When the
@@ -654,9 +656,7 @@ impl Ribbon {
let dd_open = self.open_dropdown.as_deref() == Some(COLLAPSE_MODE_ID);
let mode_btn = button(crate::ui::icons::themed_arrow_down(10.0))
.on_press(Message::ToggleRibbonDropdown(COLLAPSE_MODE_ID.to_string()))
.style(move |theme: &Theme, status| {
top_hist_btn_style(theme, true, dd_open, status)
})
.style(move |theme: &Theme, status| top_hist_btn_style(theme, true, dd_open, status))
.height(24)
.padding([2, 8]);
let mode_dd = PosReport::new(COLLAPSE_MODE_ID, mode_btn);
@@ -675,9 +675,7 @@ impl Ribbon {
let tab_bar = container(tab_row)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.palette().background.base.color,
)),
background: Some(Background::Color(theme.palette().background.base.color)),
..Default::default()
})
.padding(Padding {
@@ -732,9 +730,7 @@ impl Ribbon {
let tool_bar: Element<'_, Message> = container(tool_area)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.palette().background.weakest.color,
)),
background: Some(Background::Color(theme.palette().background.weakest.color)),
border: Border {
color: theme.palette().background.neutral.color,
width: 1.0,
@@ -755,12 +751,7 @@ impl Ribbon {
.height(Fill)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme
.palette()
.background
.strongest
.color
.scale_alpha(0.58),
theme.palette().background.strongest.color.scale_alpha(0.58),
)),
..Default::default()
}),
@@ -915,9 +906,9 @@ impl Ribbon {
RibbonItem::Dropdown {
id, items, default, ..
} => (*id, items, *default),
RibbonItem::LabeledDropdown { id, items, default, .. } => {
(*id, items, *default)
}
RibbonItem::LabeledDropdown {
id, items, default, ..
} => (*id, items, *default),
RibbonItem::LargeDropdown {
id, items, default, ..
} => (*id, items, *default),
@@ -941,24 +932,16 @@ impl Ribbon {
let disabled = widgets::is_disabled_item(cmd);
let checkmark: Element<'_, Message> =
crate::ui::icons::themed_check_cell(is_current);
let icon_el: Element<Message> =
container(make_icon(*item_icon, 20.0))
.width(Length::Fixed(20.0))
.into();
let label_el =
text(t!(*label))
.size(11)
.wrapping(iced::advanced::text::Wrapping::None)
.style(move |theme: &Theme| iced::widget::text::Style {
color: (!is_current || disabled).then_some(
theme
.palette()
.background
.base
.text
.scale_alpha(0.72),
),
});
let icon_el: Element<Message> = container(make_icon(*item_icon, 20.0))
.width(Length::Fixed(20.0))
.into();
let label_el = text(t!(*label))
.size(11)
.wrapping(iced::advanced::text::Wrapping::None)
.style(move |theme: &Theme| iced::widget::text::Style {
color: (!is_current || disabled)
.then_some(theme.palette().background.base.text.scale_alpha(0.72)),
});
button(
row![checkmark, icon_el, label_el]
@@ -1015,9 +998,7 @@ impl Ribbon {
let rows: Vec<Element<Message>> = self
.layer_infos
.iter()
.filter(|info| {
filter.is_empty() || info.name.to_lowercase().contains(&filter)
})
.filter(|info| filter.is_empty() || info.name.to_lowercase().contains(&filter))
.map(|info| {
let is_active = info.name == self.active_layer;
let lc = info.color;
@@ -1060,19 +1041,12 @@ impl Ribbon {
});
let checkmark: Element<'_, Message> =
crate::ui::icons::themed_check_cell(is_active);
let label =
text(&info.name)
.size(11)
.style(move |theme: &Theme| iced::widget::text::Style {
color: (!is_active).then_some(
theme
.palette()
.background
.base
.text
.scale_alpha(0.72),
),
});
let label = text(&info.name).size(11).style(move |theme: &Theme| {
iced::widget::text::Style {
color: (!is_active)
.then_some(theme.palette().background.base.text.scale_alpha(0.72)),
}
});
// The swatch + label area selects the layer as active; the
// icon buttons above handle their own toggles.
@@ -1135,10 +1109,10 @@ impl Ribbon {
]
.spacing(2),
)
.style(popup_panel_style)
.width(Length::Fixed(220.0));
.style(popup_panel_style)
.width(Length::Fixed(300.0));
let (align_right, h_pad, top) = self.dd_anchor(LAYER_COMBO_ID, 220.0, win.0);
let (align_right, h_pad, top) = self.dd_anchor(LAYER_COMBO_ID, 300.0, win.0);
let positioned = position_ribbon_dropdown(panel.into(), align_right, h_pad, top);
Some(dropdown_backdrop(positioned))
@@ -1190,23 +1164,17 @@ impl Ribbon {
.map(|name| {
let is_sel = name.as_str() == active.as_str();
let n = name.clone();
let checkmark: Element<Message> =
crate::ui::icons::themed_check_cell(is_sel);
let checkmark: Element<Message> = crate::ui::icons::themed_check_cell(is_sel);
button(
row![
checkmark,
text(name.clone())
.size(11)
.style(move |theme: &Theme| iced::widget::text::Style {
text(name.clone()).size(11).style(move |theme: &Theme| {
iced::widget::text::Style {
color: (!is_sel).then_some(
theme
.palette()
.background
.base
.text
.scale_alpha(0.72),
theme.palette().background.base.text.scale_alpha(0.72),
),
}),
}
}),
]
.spacing(4)
.align_y(iced::Center),
@@ -1286,19 +1254,12 @@ impl Ribbon {
.into_iter()
.map(|lt| {
let is_cur = lt.name == *active_lt;
let check: Element<'_, Message> =
crate::ui::icons::themed_check_cell(is_cur);
let check: Element<'_, Message> = crate::ui::icons::themed_check_cell(is_cur);
let name_col = text(linetype_display_name(&lt.name))
.size(11)
.style(move |theme: &Theme| iced::widget::text::Style {
color: (!is_cur).then_some(
theme
.palette()
.background
.base
.text
.scale_alpha(0.72),
),
color: (!is_cur)
.then_some(theme.palette().background.base.text.scale_alpha(0.72)),
})
.width(Length::Fixed(90.0));
let art_col = text(lt.art.clone()).size(9).style(muted_text_style);
@@ -1336,23 +1297,17 @@ impl Ribbon {
.map(|item| {
let is_cur = item.0 == active_lw;
let label = item.to_string();
let check: Element<'_, Message> =
crate::ui::icons::themed_check_cell(is_cur);
let check: Element<'_, Message> = crate::ui::icons::themed_check_cell(is_cur);
button(
row![
check,
text(label)
.size(11)
.style(move |theme: &Theme| iced::widget::text::Style {
text(label).size(11).style(move |theme: &Theme| {
iced::widget::text::Style {
color: (!is_cur).then_some(
theme
.palette()
.background
.base
.text
.scale_alpha(0.72),
theme.palette().background.base.text.scale_alpha(0.72),
),
})
}
})
]
.spacing(5)
.align_y(iced::Center),
@@ -1438,13 +1393,7 @@ fn render_group<'a>(
flush_small_col(&mut small_buf, &mut items_row);
items_row.push(render_large(item, &ctx));
} else {
small_buf.push(render_small(
item,
active_tool,
open_dd,
last_cmd,
state,
));
small_buf.push(render_small(item, active_tool, open_dd, last_cmd, state));
if small_buf.len() == 3 {
flush_small_col(&mut small_buf, &mut items_row);
}
@@ -1514,9 +1463,11 @@ fn first_tool_icon(group: &RibbonGroup) -> Option<IconKind> {
RibbonItem::LayerComboGroup { row2, .. } => row2.first().map(|t| t.icon),
RibbonItem::StyleComboGroup { rows, .. } => {
rows.first().and_then(|r| r.first()).map(|t| t.icon)
},
RibbonItem::ToolGrid { columns } => columns.first()
.and_then(|column| column.first()).map(|tool| tool.icon),
}
RibbonItem::ToolGrid { columns } => columns
.first()
.and_then(|column| column.first())
.map(|tool| tool.icon),
})
}
@@ -1600,44 +1551,42 @@ fn collapse_button<'a>(
// For a Properties panel the representative is its Match button.
let rep = representative(group, last_used);
// The face lights itself when it is the running tool.
let face_lit = rep.zip(active).is_some_and(|(item, id)| locate::item_holds(item, id));
let face_lit = rep
.zip(active)
.is_some_and(|(item, id)| locate::item_holds(item, id));
let face: Element<'_, Message> = match rep {
Some(RibbonItem::PropertiesGroup { match_prop }) => {
render_large(
&RibbonItem::LargeTool(match_prop.clone()),
&widgets::RenderCtx {
active_tool,
open_dd,
last_cmd,
state,
layer_infos,
active_layer,
active_color,
active_linetype,
active_lineweight,
style_ctx,
compact: false,
},
)
}
Some(item) => {
render_large(
item,
&widgets::RenderCtx {
active_tool,
open_dd,
last_cmd,
state,
layer_infos,
active_layer,
active_color,
active_linetype,
active_lineweight,
style_ctx,
compact: false,
},
)
}
Some(RibbonItem::PropertiesGroup { match_prop }) => render_large(
&RibbonItem::LargeTool(match_prop.clone()),
&widgets::RenderCtx {
active_tool,
open_dd,
last_cmd,
state,
layer_infos,
active_layer,
active_color,
active_linetype,
active_lineweight,
style_ctx,
compact: false,
},
),
Some(item) => render_large(
item,
&widgets::RenderCtx {
active_tool,
open_dd,
last_cmd,
state,
layer_infos,
active_layer,
active_color,
active_linetype,
active_lineweight,
style_ctx,
compact: false,
},
),
None => text("").into(),
};
@@ -1709,4 +1658,3 @@ mod tests {
assert_eq!(ribbon.open_dropdown, None);
}
}
+156 -126
View File
@@ -20,10 +20,10 @@ use iced::{
use crate::app::Message;
use crate::modules::{IconKind, ModuleEvent, RibbonItem, StyleKey, ToolDef};
use crate::ui::wrap_bar::PosReport;
use crate::t;
use crate::ui::icons;
use crate::ui::properties::{acad_color_display, linetype_display_name, LwItem};
use crate::t;
use crate::ui::wrap_bar::PosReport;
use super::LayerInfo;
@@ -110,8 +110,8 @@ fn ribbon_label_bounds(
) -> Size {
use advanced_text::{Paragraph as _, Renderer as _};
let paragraph = <iced::Renderer as advanced_text::Renderer>::Paragraph::with_text(
advanced_text::Text {
let paragraph =
<iced::Renderer as advanced_text::Renderer>::Paragraph::with_text(advanced_text::Text {
content: label,
bounds: Size::new(width, f32::INFINITY),
size: Pixels(LARGE_LABEL_SIZE),
@@ -123,8 +123,7 @@ fn ribbon_label_bounds(
wrapping,
ellipsis: advanced_text::Ellipsis::None,
hint_factor: None,
},
);
});
paragraph.min_bounds()
}
@@ -139,12 +138,7 @@ fn measure_large_width(renderer: &iced::Renderer, label: &str) -> f32 {
.0;
let max_label_height = line_height * LARGE_LABEL_LINES + 0.5;
let fits = |width: f32| {
let bounds = ribbon_label_bounds(
renderer,
label,
width,
advanced_text::Wrapping::Word,
);
let bounds = ribbon_label_bounds(renderer, label, width, advanced_text::Wrapping::Word);
bounds.width <= width + 0.5 && bounds.height <= max_label_height
};
@@ -218,11 +212,9 @@ impl Widget<Message, Theme, iced::Renderer> for AutomaticLargeWidth<'_> {
limits: &layout::Limits,
) -> layout::Node {
let width = automatic_large_width(renderer, &self.label);
self.content.as_widget_mut().layout(
tree,
renderer,
&limits.width(Length::Fixed(width)),
)
self.content
.as_widget_mut()
.layout(tree, renderer, &limits.width(Length::Fixed(width)))
}
fn operate(
@@ -247,9 +239,9 @@ impl Widget<Message, Theme, iced::Renderer> for AutomaticLargeWidth<'_> {
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
self.content.as_widget_mut().update(
tree, event, layout, cursor, renderer, shell, viewport,
);
self.content
.as_widget_mut()
.update(tree, event, layout, cursor, renderer, shell, viewport);
}
fn mouse_interaction(
@@ -371,11 +363,7 @@ pub(super) fn make_icon(icon: IconKind, size: f32) -> Element<'static, Message>
}
}
pub(super) fn is_active_tool(
id: &str,
active_tool: &Option<String>,
state: &ToggleState,
) -> bool {
pub(super) fn is_active_tool(id: &str, active_tool: &Option<String>, state: &ToggleState) -> bool {
match id {
"ORTHO" => state.ortho_mode,
"PERSP" => !state.ortho_mode,
@@ -409,8 +397,10 @@ pub(super) fn tool_btn_style(
button::Style {
background: is_active
.then_some(Background::Color(pair.color))
.or_else(|| matches!(status, button::Status::Hovered | button::Status::Pressed)
.then_some(Background::Color(pair.color))),
.or_else(|| {
matches!(status, button::Status::Hovered | button::Status::Pressed)
.then_some(Background::Color(pair.color))
}),
text_color: pair.text,
border: Border {
radius: 3.0.into(),
@@ -569,7 +559,9 @@ pub(super) fn render_small<'a>(
let content = row![
container(make_icon(t.icon, SMALL_ICON)).width(Length::Fixed(SMALL_W)),
text(label).size(10).wrapping(advanced_text::Wrapping::None),
].spacing(3).align_y(iced::Center);
]
.spacing(3)
.align_y(iced::Center);
let btn = button(content)
.on_press(Message::RibbonToolClick { tool_id, event })
.style(move |theme: &Theme, status| tool_btn_style(theme, active, status))
@@ -577,7 +569,10 @@ pub(super) fn render_small<'a>(
.height(ROW_H)
.padding([3, 4]);
tooltip(btn, make_tip(tip_text), TipPos::Right)
.gap(6.0).delay(Duration::from_millis(400)).style(tip_style).into()
.gap(6.0)
.delay(Duration::from_millis(400))
.style(tip_style)
.into()
}
RibbonItem::Dropdown {
@@ -644,9 +639,7 @@ pub(super) fn render_small<'a>(
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonDropdown(id.to_string()))
.style(move |theme: &Theme, status| {
tool_btn_style(theme, dd_open, status)
})
.style(move |theme: &Theme, status| tool_btn_style(theme, dd_open, status))
.width(Length::Fixed(ARROW_W))
.height(ROW_H)
.padding(0);
@@ -667,15 +660,30 @@ pub(super) fn render_small<'a>(
.into()
}
RibbonItem::LabeledDropdown { id, label, icon, items, default } => {
RibbonItem::LabeledDropdown {
id,
label,
icon,
items,
default,
} => {
let active = active_tool.as_deref() == Some(*id)
|| items.iter().any(|(cmd, _, _)| active_tool.as_deref() == Some(*cmd));
|| items
.iter()
.any(|(cmd, _, _)| active_tool.as_deref() == Some(*cmd));
let dd_open = open_dd.as_deref() == Some(*id);
let last = last_cmd.get(id).copied().unwrap_or(*default);
let cur_icon = last_cmd.get(id).copied().and_then(|cmd| {
items.iter().find(|(candidate, _, _)| *candidate == cmd)
.map(|(_, _, item_icon)| *item_icon)
}).or_else(|| items.first().map(|(_, _, item_icon)| *item_icon)).unwrap_or(*icon);
let cur_icon = last_cmd
.get(id)
.copied()
.and_then(|cmd| {
items
.iter()
.find(|(candidate, _, _)| *candidate == cmd)
.map(|(_, _, item_icon)| *item_icon)
})
.or_else(|| items.first().map(|(_, _, item_icon)| *item_icon))
.unwrap_or(*icon);
let localized_label = if label.is_empty() {
items
.iter()
@@ -687,8 +695,12 @@ pub(super) fn render_small<'a>(
};
let face = row![
container(make_icon(cur_icon, SMALL_ICON)).width(Length::Fixed(SMALL_W)),
text(localized_label.clone()).size(10).wrapping(advanced_text::Wrapping::None),
].spacing(3).align_y(iced::Center);
text(localized_label.clone())
.size(10)
.wrapping(advanced_text::Wrapping::None),
]
.spacing(3)
.align_y(iced::Center);
// A state that is only shown (not chosen) opens the list instead.
let face_btn = button(face)
.on_press(if is_disabled_item(last) {
@@ -700,20 +712,39 @@ pub(super) fn render_small<'a>(
}
})
.style(move |theme: &Theme, status| tool_btn_style(theme, active, status))
.width(Length::Shrink).height(ROW_H).padding([3, 4]);
let arrow = button(container(icons::themed_arrow_down(8.0))
.width(Fill).height(Fill).align_x(iced::Center).align_y(iced::Center))
.on_press(Message::ToggleRibbonDropdown(id.to_string()))
.style(move |theme: &Theme, status| tool_btn_style(theme, dd_open, status))
.width(Length::Fixed(ARROW_W)).height(ROW_H).padding(0);
.width(Length::Shrink)
.height(ROW_H)
.padding([3, 4]);
let arrow = button(
container(icons::themed_arrow_down(8.0))
.width(Fill)
.height(Fill)
.align_x(iced::Center)
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonDropdown(id.to_string()))
.style(move |theme: &Theme, status| tool_btn_style(theme, dd_open, status))
.width(Length::Fixed(ARROW_W))
.height(ROW_H)
.padding(0);
let face_tip = tool_tip_text(&localized_label, last);
let arrow_tip = format!("{} {}", localized_label, t!("options"));
PosReport::new(*id, row![
tooltip(face_btn, make_tip(face_tip), TipPos::Right)
.gap(6.0).delay(Duration::from_millis(400)).style(tip_style),
tooltip(arrow, make_tip(arrow_tip), TipPos::Right)
.gap(6.0).delay(Duration::from_millis(400)).style(tip_style),
].spacing(0).height(ROW_H)).into()
PosReport::new(
*id,
row![
tooltip(face_btn, make_tip(face_tip), TipPos::Right)
.gap(6.0)
.delay(Duration::from_millis(400))
.style(tip_style),
tooltip(arrow, make_tip(arrow_tip), TipPos::Right)
.gap(6.0)
.delay(Duration::from_millis(400))
.style(tip_style),
]
.spacing(0)
.height(ROW_H),
)
.into()
}
_ => text("").into(),
@@ -765,13 +796,23 @@ pub(super) fn render_large_dropdown<'a>(
let cur_icon = last_cmd
.get(id)
.copied()
.and_then(|cmd| items.iter().find(|(c, _, _)| *c == cmd).map(|(_, _, ik)| *ik))
.and_then(|cmd| {
items
.iter()
.find(|(c, _, _)| *c == cmd)
.map(|(_, _, ik)| *ik)
})
.or_else(|| items.first().map(|(_, _, ik)| *ik))
.unwrap_or(icon);
let cur_label = last_cmd
.get(id)
.copied()
.and_then(|cmd| items.iter().find(|(c, _, _)| *c == cmd).map(|(_, lbl, _)| *lbl))
.and_then(|cmd| {
items
.iter()
.find(|(c, _, _)| *c == cmd)
.map(|(_, lbl, _)| *lbl)
})
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
.unwrap_or(id);
let label = t!(explicit_label.unwrap_or(cur_label)).into_owned();
@@ -820,9 +861,7 @@ pub(super) fn render_large_dropdown<'a>(
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonDropdown(id.to_string()))
.style(move |theme: &Theme, status| {
tool_btn_style(theme, dd_open, status)
})
.style(move |theme: &Theme, status| tool_btn_style(theme, dd_open, status))
.width(Fill)
.height(LARGE_ARR)
.padding(0);
@@ -841,8 +880,7 @@ pub(super) fn render_large_dropdown<'a>(
.width(Fill)
.height(Fill);
PosReport::new(id, automatic_large_button(label, content.into()))
.into()
PosReport::new(id, automatic_large_button(label, content.into())).into()
}
/// A row of small tool buttons beneath a combo dropdown. Shared by the
@@ -860,9 +898,7 @@ fn tool_row<'a>(tools: &[ToolDef], active_tool: &Option<String>) -> Element<'a,
tooltip(
button(icon_el)
.on_press(msg)
.style(move |theme: &Theme, status| {
tool_btn_style(theme, is_active, status)
})
.style(move |theme: &Theme, status| tool_btn_style(theme, is_active, status))
.padding([2, 5]),
make_tip(tip.to_string()),
TipPos::Right,
@@ -889,10 +925,7 @@ fn combo_panel_col(width: f32, items: Vec<Element<'_, Message>>) -> Element<'_,
}
/// Render a full-height large button (LargeTool, LargeDropdown, LayerCombo, StyleCombo).
pub(super) fn render_large<'a>(
item: &RibbonItem,
ctx: &RenderCtx<'_>,
) -> Element<'a, Message> {
pub(super) fn render_large<'a>(item: &RibbonItem, ctx: &RenderCtx<'_>) -> Element<'a, Message> {
let active_tool = ctx.active_tool;
let open_dd = ctx.open_dd;
let last_cmd = ctx.last_cmd;
@@ -946,10 +979,10 @@ pub(super) fn render_large<'a>(
make_tip(tip_text),
TipPos::Right,
)
.gap(6.0)
.delay(Duration::from_millis(400))
.style(tip_style)
.into()
.gap(6.0)
.delay(Duration::from_millis(400))
.style(tip_style)
.into()
}
RibbonItem::LargeDropdown {
@@ -958,26 +991,37 @@ pub(super) fn render_large<'a>(
icon,
items,
default,
} => {
render_large_dropdown(*id, *icon, Some(*label), items, *default, ctx)
}
} => render_large_dropdown(*id, *icon, Some(*label), items, *default, ctx),
RibbonItem::LabeledDropdown { id, label, icon, items, default } => {
render_large_dropdown(*id, *icon, Some(*label), items, *default, ctx)
}
RibbonItem::LabeledDropdown {
id,
label,
icon,
items,
default,
} => render_large_dropdown(*id, *icon, Some(*label), items, *default, ctx),
RibbonItem::ToolGrid { columns } => columns.iter().fold(
row![].spacing(2).height(Fill).align_y(iced::Top),
|row, tools| {
let column = tools.iter().fold(
column![].spacing(2).width(Length::Fixed(SMALL_W)),
|column, tool| column.push(render_small(
&RibbonItem::Tool(tool.clone()), active_tool, open_dd, last_cmd, state,
)),
);
row.push(column)
},
).into(),
RibbonItem::ToolGrid { columns } => columns
.iter()
.fold(
row![].spacing(2).height(Fill).align_y(iced::Top),
|row, tools| {
let column = tools.iter().fold(
column![].spacing(2).width(Length::Fixed(SMALL_W)),
|column, tool| {
column.push(render_small(
&RibbonItem::Tool(tool.clone()),
active_tool,
open_dd,
last_cmd,
state,
))
},
);
row.push(column)
},
)
.into(),
// A plain Dropdown renders large too (used by a collapsed panel whose
// representative tool is a dropdown).
@@ -986,9 +1030,7 @@ pub(super) fn render_large<'a>(
icon,
items,
default,
} => {
render_large_dropdown(*id, *icon, None, items, *default, ctx)
}
} => render_large_dropdown(*id, *icon, None, items, *default, ctx),
RibbonItem::LayerComboGroup { row2, row3 } => {
const TOOL_BUTTON_W: f32 = 26.0;
@@ -998,7 +1040,9 @@ pub(super) fn render_large<'a>(
let tools_w = tool_count * TOOL_BUTTON_W
+ (tool_count - 1.0).max(0.0) * TOOL_SPACING
+ GROUP_PADDING;
let combo_w = (LARGE_W * 2.5).max(tools_w);
// Wide enough to read a typical layer name (about 25 characters)
// in the closed combo.
let combo_w = (LARGE_W * 2.5).max(tools_w).max(250.0);
let info = layer_infos.iter().find(|l| l.name == active_layer);
let lc = info.map(|l| l.color).unwrap_or(Color::WHITE);
@@ -1023,15 +1067,13 @@ pub(super) fn render_large<'a>(
.width(12)
.height(12);
const FIXED_COMBO_W: f32 =
14.0 * 3.0 + 12.0 + 9.0 + 4.0 * 4.0 + 8.0 * 2.0;
const FIXED_COMBO_W: f32 = 14.0 * 3.0 + 12.0 + 9.0 + 4.0 * 4.0 + 8.0 * 2.0;
let name_w = (combo_w - FIXED_COMBO_W).max(24.0);
// About 6 px per glyph at 11 px. The dropdown itself keeps the
// complete layer name available; only its closed ribbon label is
// shortened to preserve the fixed row height.
let name_budget = ((name_w / 6.0) as usize).max(4);
let active_layer_label =
crate::ui::text_util::elide(active_layer, name_budget);
let active_layer_label = crate::ui::text_util::elide(active_layer, name_budget);
let combo_btn = button(
row![
@@ -1048,16 +1090,16 @@ pub(super) fn render_large<'a>(
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonDropdown(LAYER_COMBO_ID.to_string()))
.style(move |theme: &Theme, status| {
combo_btn_style(theme, is_open, status, 3.0)
})
.style(move |theme: &Theme, status| combo_btn_style(theme, is_open, status, 3.0))
.padding([3, 8])
.width(Fill);
combo_panel_col(
combo_w,
vec![
container(PosReport::new(LAYER_COMBO_ID, combo_btn)).width(Fill).into(),
container(PosReport::new(LAYER_COMBO_ID, combo_btn))
.width(Fill)
.into(),
tool_row(row2, active_tool),
tool_row(row3, active_tool),
],
@@ -1109,9 +1151,7 @@ pub(super) fn render_large<'a>(
button(
row![
swatch_el,
container(text(label).size(10))
.width(Fill)
.clip(true),
container(text(label).size(10)).width(Fill).clip(true),
if is_open {
icons::themed_arrow_up(8.0)
} else {
@@ -1122,9 +1162,7 @@ pub(super) fn render_large<'a>(
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonDropdown(dd_id.to_string()))
.style(move |theme: &Theme, status| {
combo_btn_style(theme, is_open, status, 2.0)
})
.style(move |theme: &Theme, status| combo_btn_style(theme, is_open, status, 2.0))
.padding([3, 8])
.width(Length::Fixed(PROP_W))
};
@@ -1195,9 +1233,7 @@ pub(super) fn render_large<'a>(
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonDropdown(combo_id.to_string()))
.style(move |theme: &Theme, status| {
combo_btn_style(theme, is_open, status, 3.0)
})
.style(move |theme: &Theme, status| combo_btn_style(theme, is_open, status, 3.0))
.padding([3, 8])
.width(Fill);
@@ -1208,9 +1244,11 @@ pub(super) fn render_large<'a>(
iced::widget::Space::new().width(0).height(0).into();
let mut col_items: Vec<Element<Message>> =
vec![container(row![PosReport::new(*combo_id, combo_btn), items_panel].spacing(0))
.width(Fill)
.into()];
vec![
container(row![PosReport::new(*combo_id, combo_btn), items_panel].spacing(0))
.width(Fill)
.into(),
];
for row_tools in rows {
col_items.push(tool_row(row_tools, active_tool));
}
@@ -1299,9 +1337,7 @@ pub(super) fn render_history_control<'a>(
.align_x(iced::Center)
.align_y(iced::Center),
)
.style(move |theme: &Theme, status| {
top_hist_btn_style(theme, active, dd_open, status)
})
.style(move |theme: &Theme, status| top_hist_btn_style(theme, active, dd_open, status))
.width(Length::Fixed(TOP_HIST_W))
.height(24)
.padding([2, 0]);
@@ -1340,9 +1376,7 @@ pub(super) fn render_history_control<'a>(
.align_x(iced::Center)
.align_y(iced::Center),
)
.style(move |theme: &Theme, status| {
top_hist_btn_style(theme, active, dd_open, status)
})
.style(move |theme: &Theme, status| top_hist_btn_style(theme, active, dd_open, status))
.width(Length::Fixed(TOP_ARR_W))
.height(24)
.padding(0);
@@ -1353,10 +1387,7 @@ pub(super) fn render_history_control<'a>(
};
tooltip(
btn,
make_tip(format!(
"{}",
t!("%{label} history", label = t!(label))
)),
make_tip(format!("{}", t!("%{label} history", label = t!(label)))),
TipPos::Right,
)
.gap(6.0)
@@ -1382,10 +1413,9 @@ pub(super) fn top_hist_btn_style(
_ => palette.background.base,
};
button::Style {
background: (!active || open || matches!(
status,
button::Status::Hovered | button::Status::Pressed
))
background: (!active
|| open
|| matches!(status, button::Status::Hovered | button::Status::Pressed))
.then_some(Background::Color(pair.color)),
text_color: pair.text,
border: Border {