forked from 1b-agent/opencadstudio-upstream
fix(ribbon): Properties panel compact/collapse, undo-redo anchoring, restore panel dividers
- The Properties panel's Match button now shrinks to a small icon when the panel is compacted (render_large gained a `compact` flag), so the panel actually gets narrower and joins the from-the-right degradation instead of jumping straight to collapsed. - A collapsed Properties panel now shows its Match button as the representative face: item_id recognises PropertiesGroup and collapse_button renders its match_prop as a large tool. - The undo/redo history controls are wrapped in PosReport, so their dropdowns anchor directly below the button (and flip at the edge) like every other dropdown, instead of falling back to the corner. - Panel dividers are back: CollapsePanels draws a 1px line between adjacent panels, skipped only between two collapsed panels. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
5164dc1387
commit
6a5402ccae
3 files changed
+102
-36
No files matched your search
@@ -9,8 +9,11 @@ use std::cell::RefCell;
|
||||
|
||||
use iced::advanced::layout::{self, Layout};
|
||||
use iced::advanced::widget::{self, Widget};
|
||||
use iced::advanced::{mouse, overlay, renderer, Clipboard, Shell};
|
||||
use iced::{Element, Event, Length, Point, Rectangle, Renderer, Size, Theme, Vector};
|
||||
use iced::advanced::{mouse, overlay, renderer, Clipboard, Renderer as _, Shell};
|
||||
use iced::{
|
||||
Background, Border, Color, Element, Event, Length, Point, Rectangle, Renderer, Shadow, Size,
|
||||
Theme, Vector,
|
||||
};
|
||||
|
||||
use crate::app::Message;
|
||||
|
||||
@@ -35,17 +38,20 @@ pub struct CollapsePanels<'a> {
|
||||
open: Option<String>,
|
||||
/// Row height (a full ribbon tool-area height).
|
||||
row_h: f32,
|
||||
/// Colour of the 1px divider drawn between panels.
|
||||
divider: Color,
|
||||
/// Chosen degradation level per panel; set during layout.
|
||||
levels: RefCell<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'a> CollapsePanels<'a> {
|
||||
pub fn new(panels: Vec<Panel<'a>>, open: Option<String>, row_h: f32) -> Self {
|
||||
pub fn new(panels: Vec<Panel<'a>>, open: Option<String>, row_h: f32, divider: Color) -> Self {
|
||||
let n = panels.len();
|
||||
Self {
|
||||
panels,
|
||||
open,
|
||||
row_h,
|
||||
divider,
|
||||
levels: RefCell::new(vec![FULL; n]),
|
||||
}
|
||||
}
|
||||
@@ -276,6 +282,31 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
// 1px divider between adjacent panels, except between two collapsed
|
||||
// panels (whose buttons read better with no line between them).
|
||||
let bounds: Vec<Rectangle> = layout.children().map(|l| l.bounds()).collect();
|
||||
let wb = layout.bounds();
|
||||
for i in 0..self.panels.len().saturating_sub(1) {
|
||||
if levels[i] == COLLAPSED && levels[i + 1] == COLLAPSED {
|
||||
continue;
|
||||
}
|
||||
let x = bounds[i + 1].x;
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: Rectangle {
|
||||
x,
|
||||
y: wb.y,
|
||||
width: 1.0,
|
||||
height: wb.height,
|
||||
},
|
||||
border: Border::default(),
|
||||
shadow: Shadow::default(),
|
||||
snap: true,
|
||||
},
|
||||
Background::Color(self.divider),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
|
||||
+22
-3
@@ -548,7 +548,8 @@ impl Ribbon {
|
||||
.into(),
|
||||
})
|
||||
.collect();
|
||||
CollapsePanels::new(panels, self.collapsed_open.clone(), TOOL_BAR_H).into()
|
||||
CollapsePanels::new(panels, self.collapsed_open.clone(), TOOL_BAR_H, BORDER_DARK)
|
||||
.into()
|
||||
} else {
|
||||
text("").into()
|
||||
};
|
||||
@@ -1202,6 +1203,7 @@ fn render_group<'a>(
|
||||
active_linetype,
|
||||
active_lineweight,
|
||||
style_ctx,
|
||||
compact,
|
||||
));
|
||||
} else {
|
||||
small_buf.push(render_small(
|
||||
@@ -1241,6 +1243,7 @@ fn item_id(it: &RibbonItem) -> Option<&'static str> {
|
||||
match it {
|
||||
RibbonItem::Tool(t) | RibbonItem::LargeTool(t) => Some(t.id),
|
||||
RibbonItem::Dropdown { id, .. } | RibbonItem::LargeDropdown { id, .. } => Some(*id),
|
||||
RibbonItem::PropertiesGroup { match_prop } => Some(match_prop.id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1281,9 +1284,24 @@ fn collapse_button<'a>(
|
||||
) -> Element<'a, Message> {
|
||||
let title = group.title;
|
||||
|
||||
// Show the representative tool with a large icon (Tool/Dropdown alike —
|
||||
// render_large handles every tool-like item).
|
||||
// Show the representative tool with a large icon. For a Properties panel the
|
||||
// representative is its Match button, rendered as a large tool.
|
||||
let face: Element<'_, Message> = match representative(group, last_used) {
|
||||
Some(RibbonItem::PropertiesGroup { match_prop }) => render_large(
|
||||
&RibbonItem::LargeTool(match_prop.clone()),
|
||||
active_tool,
|
||||
open_dd,
|
||||
last_cmd,
|
||||
wireframe,
|
||||
ortho_mode,
|
||||
layer_infos,
|
||||
active_layer,
|
||||
active_color,
|
||||
active_linetype,
|
||||
active_lineweight,
|
||||
style_ctx,
|
||||
false,
|
||||
),
|
||||
Some(item) => render_large(
|
||||
item,
|
||||
active_tool,
|
||||
@@ -1297,6 +1315,7 @@ fn collapse_button<'a>(
|
||||
active_linetype,
|
||||
active_lineweight,
|
||||
style_ctx,
|
||||
false,
|
||||
),
|
||||
None => text("").into(),
|
||||
};
|
||||
|
||||
+46
-30
@@ -584,6 +584,8 @@ pub(super) fn render_large<'a>(
|
||||
active_linetype: &'a str,
|
||||
active_lineweight: LineWeight,
|
||||
style_ctx: &StyleContext,
|
||||
// When compact, the Properties panel's Match button shrinks to a small icon.
|
||||
compact: bool,
|
||||
) -> Element<'a, Message> {
|
||||
match item {
|
||||
// A plain Tool renders large too, so a collapsed panel can show its
|
||||
@@ -765,35 +767,49 @@ pub(super) fn render_large<'a>(
|
||||
}
|
||||
|
||||
RibbonItem::PropertiesGroup { match_prop } => {
|
||||
let mp_active = is_active_tool(match_prop.id, active_tool, wireframe, ortho_mode);
|
||||
let mp_event = match_prop.event.clone();
|
||||
let mp_id = match_prop.id.to_string();
|
||||
let mp_tip = format!("{}\nCommand: {}", match_prop.label, match_prop.id);
|
||||
let mp_btn = button(
|
||||
column![
|
||||
make_icon(match_prop.icon, LARGE_ICON),
|
||||
text(match_prop.label).size(10).color(LABEL_COLOR),
|
||||
]
|
||||
.align_x(iced::Center)
|
||||
.spacing(3),
|
||||
)
|
||||
.on_press(Message::RibbonToolClick {
|
||||
tool_id: mp_id,
|
||||
event: mp_event,
|
||||
})
|
||||
.style(move |_: &Theme, status| tool_btn_style(mp_active, status))
|
||||
.width(Length::Fixed(LARGE_W))
|
||||
.height(Fill)
|
||||
.padding(Padding {
|
||||
top: 6.0,
|
||||
right: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 4.0,
|
||||
});
|
||||
let mp_el = tooltip(mp_btn, make_tip(mp_tip), TipPos::Right)
|
||||
.gap(6.0)
|
||||
.delay(Duration::from_millis(400))
|
||||
.style(tip_style);
|
||||
// The Match Properties button is a large icon normally, but shrinks
|
||||
// to a small icon when the panel is compacted.
|
||||
let mp_el: Element<'a, Message> = if compact {
|
||||
render_small(
|
||||
&RibbonItem::Tool(match_prop.clone()),
|
||||
active_tool,
|
||||
open_dd,
|
||||
last_cmd,
|
||||
wireframe,
|
||||
ortho_mode,
|
||||
)
|
||||
} else {
|
||||
let mp_active = is_active_tool(match_prop.id, active_tool, wireframe, ortho_mode);
|
||||
let mp_event = match_prop.event.clone();
|
||||
let mp_id = match_prop.id.to_string();
|
||||
let mp_tip = format!("{}\nCommand: {}", match_prop.label, match_prop.id);
|
||||
let mp_btn = button(
|
||||
column![
|
||||
make_icon(match_prop.icon, LARGE_ICON),
|
||||
text(match_prop.label).size(10).color(LABEL_COLOR),
|
||||
]
|
||||
.align_x(iced::Center)
|
||||
.spacing(3),
|
||||
)
|
||||
.on_press(Message::RibbonToolClick {
|
||||
tool_id: mp_id,
|
||||
event: mp_event,
|
||||
})
|
||||
.style(move |_: &Theme, status| tool_btn_style(mp_active, status))
|
||||
.width(Length::Fixed(LARGE_W))
|
||||
.height(Fill)
|
||||
.padding(Padding {
|
||||
top: 6.0,
|
||||
right: 4.0,
|
||||
bottom: 4.0,
|
||||
left: 4.0,
|
||||
});
|
||||
tooltip(mp_btn, make_tip(mp_tip), TipPos::Right)
|
||||
.gap(6.0)
|
||||
.delay(Duration::from_millis(400))
|
||||
.style(tip_style)
|
||||
.into()
|
||||
};
|
||||
|
||||
const PROP_W: f32 = 130.0;
|
||||
|
||||
@@ -1139,7 +1155,7 @@ pub(super) fn render_history_control<'a>(
|
||||
.style(tip_style)
|
||||
};
|
||||
|
||||
row![main_btn, arrow_btn].spacing(0).into()
|
||||
PosReport::new(dropdown_id, row![main_btn, arrow_btn].spacing(0)).into()
|
||||
}
|
||||
|
||||
pub(super) fn top_hist_btn_style(
|
||||
|
||||
Reference in new issue
Block a user