Merge remote-tracking branch 'origin/main' into codex/sheet-set-manager

# Conflicts:
#	Cargo.lock
#	src/app/control/actions.rs
#	src/ui/window/field_dialog.rs
This commit is contained in:
ramox81 committed 2026-10-05 09:20:40 +03:00
commit 1696316e1f
95 files changed
+4269 -722

No files matched your search

+37 -3
View File
@@ -30,6 +30,7 @@ pub use context_tools::{pdf_underlay_tools, point_cloud_tools, xref_tools, Under
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};
@@ -306,6 +307,10 @@ impl Ribbon {
pub fn activate_tool(&mut self, id: &str) {
self.active_tool = Some(id.to_string());
}
#[cfg(test)]
pub(crate) fn active_tool(&self) -> Option<&str> {
self.active_tool.as_deref()
}
pub fn deactivate_tool(&mut self) {
self.active_tool = None;
}
@@ -523,6 +528,10 @@ impl Ribbon {
// The quick-access flow and the tabs flow each flex-wrap; WrapBar stacks
// them so a wrapped tab never shares a row with a quick-access button.
// A running command whose button is on another tab tints that tab's
// header with a faint wash of the button's blue, pointing at where the
// button lives without competing with the selected tab's outline.
let holding_tab = self.tab_holding_active_tool();
let tab_items = self.modules.iter().enumerate().fold(
Vec::<Element<'_, Message>>::new(),
|mut acc, (i, module)| {
@@ -534,6 +543,7 @@ impl Ribbon {
let is_active = i == self.active;
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))
@@ -546,6 +556,7 @@ impl Ribbon {
};
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
@@ -557,10 +568,17 @@ impl Ribbon {
};
button::Style {
background: (is_active
|| holds_tool
|| matches!(status, button::Status::Hovered))
.then_some(Background::Color(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 {
palette.background.base.text
} else if is_contextual {
accent.color
} else {
@@ -1421,6 +1439,7 @@ fn render_group<'a>(
group.title,
&group.tools.iter().filter_map(item_id).collect::<Vec<_>>(),
open_dd,
active_tool.as_deref(),
),
]
.align_x(iced::Center)
@@ -1502,6 +1521,19 @@ fn collapse_button<'a>(
) -> Element<'a, Message> {
let title = group.title;
let localized_title = t!(title).into_owned();
// A collapsed panel lights its opener while one of its hidden tools runs,
// the way a dropdown lights while one of its items does.
let active = active_tool.as_deref();
let holds = active.is_some_and(|id| locate::group_holds(group, id));
let opener_style = move |lit: bool| {
move |theme: &Theme, status| {
if lit {
tool_btn_style(theme, true, status)
} else {
button::subtle(theme, status)
}
}
};
// Tightest form: one button = the panel's FIRST tool icon + its title + ▾.
// Clicking opens the flyout listing every tool; no tool runs directly at this
@@ -1532,7 +1564,7 @@ fn collapse_button<'a>(
.width(Fill),
)
.on_press(Message::ToggleRibbonPanel(title.to_string()))
.style(button::subtle)
.style(opener_style(holds))
.width(Fill)
.padding([3, 5]);
return automatic_large_button(localized_title, content.into());
@@ -1542,6 +1574,8 @@ fn collapse_button<'a>(
// that runs the last-used tool — above a title + ▾ opener for the full flyout.
// 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: Element<'_, Message> = match rep {
Some(RibbonItem::PropertiesGroup { match_prop }) => {
render_large(
@@ -1597,7 +1631,7 @@ fn collapse_button<'a>(
.align_y(iced::Center),
)
.on_press(Message::ToggleRibbonPanel(title.to_string()))
.style(button::subtle)
.style(opener_style(holds && !face_lit))
.width(Fill)
.padding([1, 4]);
let opener = automatic_large_button(localized_title, opener.into());