forked from 1b-agent/opencadstudio-upstream
Share centered extension panels with Modify tool contributions
This commit is contained in:
1 parent
75cc49d629
commit
09c1910f6a
4 files changed
+78
-27
No files matched your search
@@ -4,6 +4,7 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
generate_modify_tools();
|
||||||
let version = std::env::var("CARGO_PKG_VERSION").expect("Cargo package version");
|
let version = std::env::var("CARGO_PKG_VERSION").expect("Cargo package version");
|
||||||
let parts: Vec<&str> = version.split('.').collect();
|
let parts: Vec<&str> = version.split('.').collect();
|
||||||
let app_version = if parts.len() == 3 && parts[0].len() == 4
|
let app_version = if parts.len() == 3 && parts[0].len() == 4
|
||||||
@@ -81,3 +82,24 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_modify_tools() {
|
||||||
|
let root = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||||
|
let directory = root.join("src/ui/ribbon/modify_tools");
|
||||||
|
println!("cargo:rerun-if-changed=src/ui/ribbon/modify_tools");
|
||||||
|
let mut paths: Vec<_> = match std::fs::read_dir(&directory) {
|
||||||
|
Ok(entries) => entries.map(|entry| entry.expect("Modify tool entry").path())
|
||||||
|
.filter(|path| path.extension().is_some_and(|ext| ext == "rs")).collect(),
|
||||||
|
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Vec::new(),
|
||||||
|
Err(error) => panic!("Cannot read Modify tools: {error}"),
|
||||||
|
};
|
||||||
|
paths.sort();
|
||||||
|
let mut source = String::from("pub(super) const TOOLS: &[Tool] = &[\n");
|
||||||
|
for path in paths {
|
||||||
|
println!("cargo:rerun-if-changed={}", path.display());
|
||||||
|
source.push_str(&format!("include!({:?}),\n", path.to_string_lossy()));
|
||||||
|
}
|
||||||
|
source.push_str("];\n");
|
||||||
|
let output = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||||
|
std::fs::write(output.join("modify_panel_tools.rs"), source).expect("Write Modify tool registry");
|
||||||
|
}
|
||||||
+47
-23
@@ -22,11 +22,11 @@ const CELL: f32 = 36.0 * SCALE;
|
|||||||
const GAP: f32 = 3.0 * SCALE;
|
const GAP: f32 = 3.0 * SCALE;
|
||||||
const OPTION_HEIGHT: f32 = 30.0 * SCALE;
|
const OPTION_HEIGHT: f32 = 30.0 * SCALE;
|
||||||
|
|
||||||
struct Tool {
|
pub(super) struct Tool {
|
||||||
command: &'static str,
|
pub command: &'static str,
|
||||||
label: &'static str,
|
pub label: &'static str,
|
||||||
icon: &'static [u8],
|
pub icon: &'static [u8],
|
||||||
options: &'static [(&'static str, &'static str)],
|
pub options: &'static [(&'static str, &'static str)],
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOOLS: &[Tool] = &[
|
const TOOLS: &[Tool] = &[
|
||||||
@@ -118,43 +118,63 @@ const TOOLS: &[Tool] = &[
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Panel {
|
||||||
|
id: &'static str,
|
||||||
|
title_id: &'static str,
|
||||||
|
title: &'static str,
|
||||||
|
tools: &'static [Tool],
|
||||||
|
}
|
||||||
|
|
||||||
|
const PANELS: &[Panel] = &[
|
||||||
|
Panel { id: PANEL_ID, title_id: TITLE_ID, title: "Draw", tools: TOOLS },
|
||||||
|
Panel { id: "modify_extension", title_id: "modify_extension_title", title: "Modify", tools: super::modify_panel::TOOLS },
|
||||||
|
];
|
||||||
|
|
||||||
|
fn panel_for_dropdown(id: &str) -> Option<Panel> {
|
||||||
|
PANELS.iter().copied().find(|panel| {
|
||||||
|
panel.id == id || panel.tools.iter().any(|tool| tool.command == id && !tool.options.is_empty())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn owns_dropdown(id: &str) -> bool {
|
pub(super) fn owns_dropdown(id: &str) -> bool {
|
||||||
id == PANEL_ID
|
panel_for_dropdown(id).is_some()
|
||||||
|| TOOLS
|
}
|
||||||
.iter()
|
|
||||||
.any(|tool| tool.command == id && !tool.options.is_empty())
|
pub(super) fn parent_panel(id: &str) -> Option<&'static str> {
|
||||||
|
panel_for_dropdown(id).map(|panel| panel.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn group_title<'a>(title: &'static str, open: &Option<String>) -> Element<'a, Message> {
|
pub(super) fn group_title<'a>(title: &'static str, open: &Option<String>) -> Element<'a, Message> {
|
||||||
if title != "Draw" || TOOLS.is_empty() {
|
let Some(panel) = PANELS.iter().find(|panel| panel.title == title && !panel.tools.is_empty()) else {
|
||||||
return container(text(t!(title)).size(9).style(muted_text_style))
|
return container(text(t!(title)).size(9).style(muted_text_style))
|
||||||
.padding([1, 4])
|
.padding([1, 4])
|
||||||
.into();
|
.into();
|
||||||
}
|
};
|
||||||
let expanded = open.as_deref().is_some_and(owns_dropdown);
|
let expanded = open.as_deref().and_then(parent_panel) == Some(panel.id);
|
||||||
let arrow = if expanded {
|
let arrow = if expanded {
|
||||||
icons::themed_arrow_up(7.0)
|
icons::themed_arrow_up(7.0)
|
||||||
} else {
|
} else {
|
||||||
icons::themed_arrow_down(7.0)
|
icons::themed_arrow_down(7.0)
|
||||||
};
|
};
|
||||||
PosReport::new(
|
PosReport::new(
|
||||||
PANEL_ID,
|
panel.id,
|
||||||
button(
|
button(
|
||||||
row![PosReport::new(TITLE_ID, text(t!(title)).size(9)), arrow]
|
row![PosReport::new(panel.title_id, text(t!(title)).size(9)), arrow]
|
||||||
.spacing(4)
|
.spacing(4)
|
||||||
.align_y(iced::Center),
|
.align_y(iced::Center),
|
||||||
)
|
)
|
||||||
.on_press(Message::ToggleRibbonDropdown(PANEL_ID.to_string()))
|
.on_press(Message::ToggleRibbonDropdown(panel.id.to_string()))
|
||||||
.style(move |theme: &Theme, status| tool_btn_style(theme, expanded, status))
|
.style(move |theme: &Theme, status| tool_btn_style(theme, expanded, status))
|
||||||
.padding([1, 4]),
|
.padding([1, 4]),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tool_button(tool: &Tool, active: bool) -> Element<'static, Message> {
|
fn tool_button(tool: &Tool, active: bool, panel_id: &'static str) -> Element<'static, Message> {
|
||||||
let face = button(make_icon(IconKind::Svg(tool.icon), 23.0 * SCALE))
|
let face = button(make_icon(IconKind::Svg(tool.icon), 23.0 * SCALE))
|
||||||
.on_press(Message::DropdownSelectItem {
|
.on_press(Message::DropdownSelectItem {
|
||||||
dropdown_id: PANEL_ID,
|
dropdown_id: panel_id,
|
||||||
cmd: tool.command,
|
cmd: tool.command,
|
||||||
})
|
})
|
||||||
.style(move |theme: &Theme, status| tool_btn_style(theme, active, status))
|
.style(move |theme: &Theme, status| tool_btn_style(theme, active, status))
|
||||||
@@ -187,22 +207,26 @@ fn tool_button(tool: &Tool, active: bool) -> Element<'static, Message> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn overlay<'a>(ribbon: &Ribbon, id: &str, win: (f32, f32)) -> Element<'a, Message> {
|
pub(super) fn overlay<'a>(ribbon: &Ribbon, id: &str, win: (f32, f32)) -> Element<'a, Message> {
|
||||||
|
let panel = panel_for_dropdown(id).expect("registered ribbon extension");
|
||||||
|
let tools = panel.tools;
|
||||||
let width = (7.0 * (CELL + GAP) - GAP + 12.0).min((win.0 - 8.0).max(CELL + 12.0));
|
let width = (7.0 * (CELL + GAP) - GAP + 12.0).min((win.0 - 8.0).max(CELL + 12.0));
|
||||||
let (_, x, anchor_top) = ribbon.dd_anchor(PANEL_ID, width, win.0);
|
let (_, x, anchor_top) = ribbon.dd_anchor(panel.id, width, win.0);
|
||||||
let anchor = crate::ui::wrap_bar::dropdown_bounds(TITLE_ID);
|
let anchor = crate::ui::wrap_bar::dropdown_bounds(panel.title_id);
|
||||||
let left = anchor
|
let left = anchor
|
||||||
.map_or(x, |b| b.x + (b.width - width) / 2.0)
|
.map_or(x, |b| b.x + (b.width - width) / 2.0)
|
||||||
.clamp(0.0, (win.0 - width).max(0.0));
|
.clamp(0.0, (win.0 - width).max(0.0));
|
||||||
let top = anchor_top.min((win.1 - 80.0).max(0.0));
|
let top = anchor_top.min((win.1 - 80.0).max(0.0));
|
||||||
let available_height = (win.1 - top - 4.0).max(1.0);
|
let available_height = (win.1 - top - 4.0).max(1.0);
|
||||||
let cols = (((width - 12.0 + GAP) / (CELL + GAP)).floor() as usize).clamp(1, 7);
|
let cols = (((width - 12.0 + GAP) / (CELL + GAP)).floor() as usize).clamp(1, 7);
|
||||||
let option_tool = TOOLS
|
let option_tool = tools
|
||||||
.iter()
|
.iter()
|
||||||
.find(|tool| tool.command == id && !tool.options.is_empty());
|
.find(|tool| tool.command == id && !tool.options.is_empty());
|
||||||
let content_height = option_tool.map_or_else(
|
let content_height = option_tool.map_or_else(
|
||||||
|| TOOLS.len().div_ceil(cols) as f32 * (CELL + GAP) - GAP + 12.0,
|
|| tools.len().div_ceil(cols) as f32 * (CELL + GAP) - GAP + 12.0,
|
||||||
|tool| tool.options.len() as f32 * OPTION_HEIGHT,
|
|tool| tool.options.len() as f32 * OPTION_HEIGHT,
|
||||||
);
|
);
|
||||||
|
let ordered: Vec<&Tool> = tools.iter().filter(|tool| tool.options.is_empty())
|
||||||
|
.chain(tools.iter().filter(|tool| !tool.options.is_empty())).collect();
|
||||||
let contents: Element<'static, Message> = if let Some(tool) = option_tool {
|
let contents: Element<'static, Message> = if let Some(tool) = option_tool {
|
||||||
column(
|
column(
|
||||||
tool.options
|
tool.options
|
||||||
@@ -224,13 +248,13 @@ pub(super) fn overlay<'a>(ribbon: &Ribbon, id: &str, win: (f32, f32)) -> Element
|
|||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
column(
|
column(
|
||||||
TOOLS
|
ordered
|
||||||
.chunks(cols)
|
.chunks(cols)
|
||||||
.map(|tools| {
|
.map(|tools| {
|
||||||
row(tools
|
row(tools
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tool| {
|
.map(|tool| {
|
||||||
tool_button(tool, ribbon.active_tool.as_deref() == Some(tool.command))
|
tool_button(tool, ribbon.active_tool.as_deref() == Some(tool.command), panel.id)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>())
|
||||||
.spacing(GAP)
|
.spacing(GAP)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use crate::ui::properties::{linetype_display_name, lw_options, LinetypeItem};
|
|||||||
|
|
||||||
mod widgets;
|
mod widgets;
|
||||||
mod draw_panel;
|
mod draw_panel;
|
||||||
|
mod modify_panel;
|
||||||
use widgets::{StyleContext, *};
|
use widgets::{StyleContext, *};
|
||||||
mod collapse;
|
mod collapse;
|
||||||
use collapse::{CollapsePanels, Panel};
|
use collapse::{CollapsePanels, Panel};
|
||||||
@@ -355,13 +356,13 @@ impl Ribbon {
|
|||||||
let Some(id) = self.open_dropdown.as_deref() else {
|
let Some(id) = self.open_dropdown.as_deref() else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if !draw_panel::owns_dropdown(id) {
|
let Some(parent) = draw_panel::parent_panel(id) else {
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
if id == draw_panel::PANEL_ID {
|
if id == parent {
|
||||||
self.close_dropdown();
|
self.close_dropdown();
|
||||||
} else {
|
} else {
|
||||||
self.open_dropdown = Some(draw_panel::PANEL_ID.to_string());
|
self.open_dropdown = Some(parent.to_string());
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//! Independently registered commands in the Modify extension.
|
||||||
|
use super::draw_panel::Tool;
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/modify_panel_tools.rs"));
|
||||||
Reference in new issue
Block a user