forked from 1b-agent/opencadstudio-upstream
Add Graphic Attributes fill and line controls
This commit is contained in:
1 parent
65bd8e1b4c
commit
abef285cc6
16 files changed
+2112
-891
No files matched your search
@@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="#B4B6B9"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
version="1.1"
|
||||||
|
id="svg3"
|
||||||
|
sodipodi:docname="lineweight.svg"
|
||||||
|
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs3" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview3"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="6.0364583"
|
||||||
|
inkscape:cx="-23.606557"
|
||||||
|
inkscape:cy="-24.434858"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1368"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg3" />
|
||||||
|
<path
|
||||||
|
d="M 0,0.75 H 24.000001"
|
||||||
|
stroke-width="1.1"
|
||||||
|
id="path1"
|
||||||
|
style="stroke-width:1.5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="M 0,5 H 24.000002"
|
||||||
|
stroke-width="2.4"
|
||||||
|
id="path2-6-6"
|
||||||
|
style="stroke-width:3;stroke-linecap:butt;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="m 2e-6,11.75 h 24"
|
||||||
|
stroke-width="2.4"
|
||||||
|
id="path2-6-2-1"
|
||||||
|
style="stroke-width:5.5;stroke-linecap:butt;stroke-miterlimit:0;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="M 0,20.5 H 24"
|
||||||
|
stroke-width="2.4"
|
||||||
|
id="path2-6-2-1-6"
|
||||||
|
style="stroke-width:7;stroke-linecap:butt;stroke-miterlimit:0;stroke-dasharray:none" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="#B4B6B9"
|
||||||
|
stroke-width="1.8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="transparency.svg"
|
||||||
|
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="34.147365"
|
||||||
|
inkscape:cx="13.368528"
|
||||||
|
inkscape:cy="10.527899"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1368"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1" />
|
||||||
|
<rect
|
||||||
|
x="0.89999998"
|
||||||
|
y="0.89999998"
|
||||||
|
width="22.200001"
|
||||||
|
height="22.200001"
|
||||||
|
id="rect1"
|
||||||
|
style="stroke-width:1.8;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="M 0.89999998,0.89999998 H 12 V 12 H 0.89999998 Z M 12,12 H 23.1 V 23.1 H 12 Z"
|
||||||
|
fill="#b4b6b9"
|
||||||
|
stroke="none"
|
||||||
|
id="path1"
|
||||||
|
style="stroke-width:1.8" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -770,6 +770,12 @@ pub(super) struct OpenCADStudio {
|
|||||||
show_properties: bool,
|
show_properties: bool,
|
||||||
show_graphic_attributes: bool,
|
show_graphic_attributes: bool,
|
||||||
graphic_attribute_menu_open: bool,
|
graphic_attribute_menu_open: bool,
|
||||||
|
line_color_menu_open: bool,
|
||||||
|
line_linetype_menu_open: bool,
|
||||||
|
line_lineweight_menu_open: bool,
|
||||||
|
line_transparency_menu_open: bool,
|
||||||
|
graphic_hatch_pattern_search: String,
|
||||||
|
graphic_hatch_pattern_focus: usize,
|
||||||
solid_fill_color_menu_open: bool,
|
solid_fill_color_menu_open: bool,
|
||||||
fill_transparency_menu_open: bool,
|
fill_transparency_menu_open: bool,
|
||||||
gradient_editor: Option<crate::ui::window::gradient_editor::GradientEditorState>,
|
gradient_editor: Option<crate::ui::window::gradient_editor::GradientEditorState>,
|
||||||
@@ -1531,6 +1537,7 @@ pub enum ColorPickerTab {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ColorPickTarget {
|
pub enum ColorPickTarget {
|
||||||
Gradient(u8),
|
Gradient(u8),
|
||||||
|
GraphicAttributesLine,
|
||||||
GraphicAttributesSolid,
|
GraphicAttributesSolid,
|
||||||
DimStyle(DsField),
|
DimStyle(DsField),
|
||||||
MLeader(&'static str),
|
MLeader(&'static str),
|
||||||
@@ -2057,6 +2064,22 @@ pub enum Message {
|
|||||||
ToggleGraphicAttributeDropdown,
|
ToggleGraphicAttributeDropdown,
|
||||||
CloseGraphicAttributeDropdown,
|
CloseGraphicAttributeDropdown,
|
||||||
GraphicAttributeChanged(crate::ui::window::graphic_attributes::GraphicAttribute),
|
GraphicAttributeChanged(crate::ui::window::graphic_attributes::GraphicAttribute),
|
||||||
|
ToggleLineColorDropdown,
|
||||||
|
CloseLineColorDropdown,
|
||||||
|
LineColorChanged(AcadColor),
|
||||||
|
ToggleLineLinetypeMenu,
|
||||||
|
CloseLineLinetypeMenu,
|
||||||
|
LineLinetypeChanged(String),
|
||||||
|
ToggleLineLineweightMenu,
|
||||||
|
CloseLineLineweightMenu,
|
||||||
|
LineLineweightChanged(LineWeight),
|
||||||
|
LineLinetypeScaleChanged(f64),
|
||||||
|
LineLinetypeScaleInput(String),
|
||||||
|
ToggleLineTransparencyDropdown,
|
||||||
|
CloseLineTransparencyDropdown,
|
||||||
|
LineTransparencyChanged(u8),
|
||||||
|
LineTransparencyByLayer,
|
||||||
|
LineTransparencyByBlock,
|
||||||
ToggleSolidFillColorDropdown,
|
ToggleSolidFillColorDropdown,
|
||||||
CloseSolidFillColorDropdown,
|
CloseSolidFillColorDropdown,
|
||||||
SolidFillColorChanged(AcadColor),
|
SolidFillColorChanged(AcadColor),
|
||||||
@@ -2067,6 +2090,10 @@ pub enum Message {
|
|||||||
FillTransparencyByBlock,
|
FillTransparencyByBlock,
|
||||||
HatchEditorOpen,
|
HatchEditorOpen,
|
||||||
HatchEditorClose,
|
HatchEditorClose,
|
||||||
|
GraphicHatchPatternSearchChanged(String),
|
||||||
|
GraphicHatchPatternFocus(usize),
|
||||||
|
GraphicHatchPatternChanged(String),
|
||||||
|
GraphicHatchPatternConfirm,
|
||||||
GradientEditorOpen,
|
GradientEditorOpen,
|
||||||
GradientColorModeChanged(crate::ui::window::gradient_editor::GradientColorMode),
|
GradientColorModeChanged(crate::ui::window::gradient_editor::GradientColorMode),
|
||||||
GradientColorToggle(u8),
|
GradientColorToggle(u8),
|
||||||
@@ -4130,6 +4157,12 @@ impl OpenCADStudio {
|
|||||||
show_properties: true,
|
show_properties: true,
|
||||||
show_graphic_attributes: true,
|
show_graphic_attributes: true,
|
||||||
graphic_attribute_menu_open: false,
|
graphic_attribute_menu_open: false,
|
||||||
|
line_color_menu_open: false,
|
||||||
|
line_linetype_menu_open: false,
|
||||||
|
line_lineweight_menu_open: false,
|
||||||
|
line_transparency_menu_open: false,
|
||||||
|
graphic_hatch_pattern_search: String::new(),
|
||||||
|
graphic_hatch_pattern_focus: 0,
|
||||||
solid_fill_color_menu_open: false,
|
solid_fill_color_menu_open: false,
|
||||||
fill_transparency_menu_open: false,
|
fill_transparency_menu_open: false,
|
||||||
gradient_editor: None,
|
gradient_editor: None,
|
||||||
|
|||||||
+320
-357
File diff suppressed because it is too large.
Load diff
+373
-29
@@ -34,12 +34,15 @@ fn set_hatch_graphic_attribute(
|
|||||||
hatch.gradient_color.enabled = false;
|
hatch.gradient_color.enabled = false;
|
||||||
}
|
}
|
||||||
GraphicAttribute::Hatch => {
|
GraphicAttribute::Hatch => {
|
||||||
let Some(entry) = crate::scene::model::hatch_patterns::catalog().iter().find(|entry| {
|
let Some(entry) = crate::scene::model::hatch_patterns::catalog()
|
||||||
|
.iter()
|
||||||
|
.find(|entry| {
|
||||||
matches!(
|
matches!(
|
||||||
entry.gpu,
|
entry.gpu,
|
||||||
crate::scene::model::hatch_model::HatchPattern::Pattern(_)
|
crate::scene::model::hatch_model::HatchPattern::Pattern(_)
|
||||||
)
|
)
|
||||||
}) else {
|
})
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut pattern = crate::scene::model::hatch_patterns::build_dxf_pattern(entry);
|
let mut pattern = crate::scene::model::hatch_patterns::build_dxf_pattern(entry);
|
||||||
@@ -49,11 +52,7 @@ fn set_hatch_graphic_attribute(
|
|||||||
);
|
);
|
||||||
crate::entities::hatch::rotate_pattern_geometry(&mut pattern, hatch.pattern_angle);
|
crate::entities::hatch::rotate_pattern_geometry(&mut pattern, hatch.pattern_angle);
|
||||||
let origin = hatch.pattern_origin();
|
let origin = hatch.pattern_origin();
|
||||||
crate::entities::hatch::translate_pattern_geometry(
|
crate::entities::hatch::translate_pattern_geometry(&mut pattern, origin.x, origin.y);
|
||||||
&mut pattern,
|
|
||||||
origin.x,
|
|
||||||
origin.y,
|
|
||||||
);
|
|
||||||
hatch.pattern = pattern;
|
hatch.pattern = pattern;
|
||||||
hatch.pattern_type = acadrust::entities::HatchPatternType::Predefined;
|
hatch.pattern_type = acadrust::entities::HatchPatternType::Predefined;
|
||||||
hatch.is_solid = false;
|
hatch.is_solid = false;
|
||||||
@@ -200,10 +199,16 @@ impl OpenCADStudio {
|
|||||||
use crate::scene::model::hatch_model::{GradientKind, HatchPattern};
|
use crate::scene::model::hatch_model::{GradientKind, HatchPattern};
|
||||||
use crate::ui::window::graphic_attributes::GraphicAttribute;
|
use crate::ui::window::graphic_attributes::GraphicAttribute;
|
||||||
self.graphic_attribute_menu_open = false;
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
self.fill_transparency_menu_open = false;
|
self.fill_transparency_menu_open = false;
|
||||||
self.gradient_editor = None;
|
self.gradient_editor = None;
|
||||||
self.hatch_editor_handles = None;
|
self.hatch_editor_handles = None;
|
||||||
|
self.graphic_hatch_pattern_search.clear();
|
||||||
|
self.graphic_hatch_pattern_focus = 0;
|
||||||
if value == GraphicAttribute::Varies {
|
if value == GraphicAttribute::Varies {
|
||||||
return iced::Task::none();
|
return iced::Task::none();
|
||||||
}
|
}
|
||||||
@@ -220,8 +225,8 @@ impl OpenCADStudio {
|
|||||||
return iced::Task::none();
|
return iced::Task::none();
|
||||||
}
|
}
|
||||||
let selected_handles: rustc_hash::FxHashSet<_> = selected.iter().copied().collect();
|
let selected_handles: rustc_hash::FxHashSet<_> = selected.iter().copied().collect();
|
||||||
let (associated, new_hatches, updated_hatches, skipped) =
|
let (associated, new_hatches, updated_hatches, skipped) = if value == GraphicAttribute::None
|
||||||
if value == GraphicAttribute::None {
|
{
|
||||||
let associated: Vec<_> = self.tabs[i]
|
let associated: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.document
|
.document
|
||||||
@@ -294,7 +299,8 @@ impl OpenCADStudio {
|
|||||||
self.tabs[i].scene.document.get_entity(*handle)
|
self.tabs[i].scene.document.get_entity(*handle)
|
||||||
{
|
{
|
||||||
let mut hatch = hatch.clone();
|
let mut hatch = hatch.clone();
|
||||||
let visible_color = crate::scene::view::render::render_style_for_common_viewport(
|
let visible_color =
|
||||||
|
crate::scene::view::render::render_style_for_common_viewport(
|
||||||
&self.tabs[i].scene.document,
|
&self.tabs[i].scene.document,
|
||||||
&hatch.common,
|
&hatch.common,
|
||||||
None,
|
None,
|
||||||
@@ -402,7 +408,7 @@ impl OpenCADStudio {
|
|||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let selected = self.tabs[i].scene.selected_handles_in_order();
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
let handles = crate::ui::window::graphic_attributes::solid_fill_handles(
|
let handles = crate::ui::window::graphic_attributes::fill_handles(
|
||||||
&self.tabs[i].scene.document,
|
&self.tabs[i].scene.document,
|
||||||
&selected,
|
&selected,
|
||||||
);
|
);
|
||||||
@@ -410,7 +416,7 @@ impl OpenCADStudio {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||||
.collect();
|
.collect();
|
||||||
self.apply_property_op(i, "Solid fill color", &unlocked, |app, handle| {
|
self.apply_property_op(i, "Fill color", &unlocked, |app, handle| {
|
||||||
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
crate::scene::view::dispatch::apply_color(entity, color);
|
crate::scene::view::dispatch::apply_color(entity, color);
|
||||||
}
|
}
|
||||||
@@ -418,6 +424,146 @@ impl OpenCADStudio {
|
|||||||
iced::Task::none()
|
iced::Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_line_color(&mut self, color: acadrust::types::Color) -> iced::Task<Message> {
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
|
if selected.is_empty() {
|
||||||
|
return self.on_ribbon_color_changed(color);
|
||||||
|
}
|
||||||
|
let handles = crate::ui::window::graphic_attributes::line_handles(
|
||||||
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
|
);
|
||||||
|
let unlocked: Vec<_> = handles
|
||||||
|
.into_iter()
|
||||||
|
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||||
|
.collect();
|
||||||
|
self.apply_property_op(i, "Line color", &unlocked, |app, handle| {
|
||||||
|
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
|
crate::scene::view::dispatch::apply_color(entity, color);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iced::Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_line_linetype(&mut self, linetype: String) -> iced::Task<Message> {
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
|
if selected.is_empty() {
|
||||||
|
return self.on_ribbon_linetype_changed(linetype);
|
||||||
|
}
|
||||||
|
let handles = crate::ui::window::graphic_attributes::line_handles(
|
||||||
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
|
);
|
||||||
|
let unlocked: Vec<_> = handles
|
||||||
|
.into_iter()
|
||||||
|
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||||
|
.collect();
|
||||||
|
self.apply_property_op(i, "Line linetype", &unlocked, |app, handle| {
|
||||||
|
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
|
crate::scene::view::dispatch::apply_common_prop(entity, "linetype", &linetype);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iced::Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_line_lineweight(
|
||||||
|
&mut self,
|
||||||
|
lineweight: acadrust::types::LineWeight,
|
||||||
|
) -> iced::Task<Message> {
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
|
if selected.is_empty() {
|
||||||
|
self.tabs[i].scene.document.header.current_line_weight = lineweight.value();
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.ribbon.active_lineweight = lineweight;
|
||||||
|
self.refresh_properties();
|
||||||
|
return iced::Task::none();
|
||||||
|
}
|
||||||
|
let handles = crate::ui::window::graphic_attributes::line_handles(
|
||||||
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
|
);
|
||||||
|
let unlocked: Vec<_> = handles
|
||||||
|
.into_iter()
|
||||||
|
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||||
|
.collect();
|
||||||
|
self.apply_property_op(i, "Line weight", &unlocked, |app, handle| {
|
||||||
|
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
|
crate::scene::view::dispatch::apply_line_weight(entity, lineweight);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iced::Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_line_linetype_scale(&mut self, scale: f64) -> iced::Task<Message> {
|
||||||
|
let scale = scale.clamp(0.01, 1000.0);
|
||||||
|
let i = self.active_tab;
|
||||||
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
|
if selected.is_empty() {
|
||||||
|
self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.header
|
||||||
|
.current_entity_linetype_scale = scale;
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.refresh_properties();
|
||||||
|
return iced::Task::none();
|
||||||
|
}
|
||||||
|
let handles = crate::ui::window::graphic_attributes::line_handles(
|
||||||
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
|
);
|
||||||
|
let unlocked: Vec<_> = handles
|
||||||
|
.into_iter()
|
||||||
|
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||||
|
.collect();
|
||||||
|
self.apply_property_op(i, "Linetype scale", &unlocked, |app, handle| {
|
||||||
|
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
|
entity.common_mut().linetype_scale = scale;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iced::Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_line_transparency(
|
||||||
|
&mut self,
|
||||||
|
transparency: acadrust::types::Transparency,
|
||||||
|
) -> iced::Task<Message> {
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
|
if selected.is_empty() {
|
||||||
|
if self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.set_current_entity_transparency(transparency)
|
||||||
|
{
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.refresh_properties();
|
||||||
|
}
|
||||||
|
return iced::Task::none();
|
||||||
|
}
|
||||||
|
let handles = crate::ui::window::graphic_attributes::line_handles(
|
||||||
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
|
);
|
||||||
|
let unlocked: Vec<_> = handles
|
||||||
|
.into_iter()
|
||||||
|
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||||
|
.collect();
|
||||||
|
self.apply_property_op(i, "Line transparency", &unlocked, |app, handle| {
|
||||||
|
if let Some(entity) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||||
|
entity.common_mut().transparency = transparency;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iced::Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn on_fill_transparency(
|
pub(super) fn on_fill_transparency(
|
||||||
&mut self,
|
&mut self,
|
||||||
transparency: acadrust::types::Transparency,
|
transparency: acadrust::types::Transparency,
|
||||||
@@ -520,8 +666,11 @@ impl OpenCADStudio {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_ribbon_tool_click(
|
||||||
pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEvent) -> Task<Message> {
|
&mut self,
|
||||||
|
tool_id: String,
|
||||||
|
event: ModuleEvent,
|
||||||
|
) -> Task<Message> {
|
||||||
// Commands use `start_allowed`; other events need a drawing
|
// Commands use `start_allowed`; other events need a drawing
|
||||||
// and stay blocked on the Start page (#299, #388, #389).
|
// and stay blocked on the Start page (#299, #388, #389).
|
||||||
if self.tabs[self.active_tab].is_start && !matches!(event, ModuleEvent::Command(_)) {
|
if self.tabs[self.active_tab].is_start && !matches!(event, ModuleEvent::Command(_)) {
|
||||||
@@ -563,7 +712,8 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.tabs[i].scene.clear();
|
self.tabs[i].scene.clear();
|
||||||
self.tabs[i].properties = PropertiesPanel::empty();
|
self.tabs[i].properties = PropertiesPanel::empty();
|
||||||
self.command_line.push_output(crate::t!("Scene cleared.").as_ref());
|
self.command_line
|
||||||
|
.push_output(crate::t!("Scene cleared.").as_ref());
|
||||||
}
|
}
|
||||||
ModuleEvent::SetVisualStyle(name) => {
|
ModuleEvent::SetVisualStyle(name) => {
|
||||||
use crate::modules::view::visual_style;
|
use crate::modules::view::visual_style;
|
||||||
@@ -572,9 +722,8 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
None => {
|
None => {
|
||||||
// Name the styles that do exist, from the same
|
// Name the styles that do exist, from the same
|
||||||
// list every other caller reads.
|
// list every other caller reads.
|
||||||
self.command_line.push_error(
|
self.command_line
|
||||||
crate::tf!("Unknown visual style \"{name}\".").as_ref(),
|
.push_error(crate::tf!("Unknown visual style \"{name}\".").as_ref());
|
||||||
);
|
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_info(crate::t!(visual_style::keyword_prompt()).as_ref());
|
.push_info(crate::t!(visual_style::keyword_prompt()).as_ref());
|
||||||
}
|
}
|
||||||
@@ -591,8 +740,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
} => {
|
} => {
|
||||||
return Task::perform(
|
return Task::perform(
|
||||||
async move {
|
async move {
|
||||||
let exts: Vec<&str> =
|
let exts: Vec<&str> = extensions.iter().map(|s| s.as_str()).collect();
|
||||||
extensions.iter().map(|s| s.as_str()).collect();
|
|
||||||
let path = crate::sys::file_dialog()
|
let path = crate::sys::file_dialog()
|
||||||
.set_title(title)
|
.set_title(title)
|
||||||
.add_filter(filter_name, &exts)
|
.add_filter(filter_name, &exts)
|
||||||
@@ -622,8 +770,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
let _ = std::fs::remove_file(self.autosave_target(idx));
|
let _ = std::fs::remove_file(self.autosave_target(idx));
|
||||||
if self.tabs.len() == 1 {
|
if self.tabs.len() == 1 {
|
||||||
self.tab_counter += 1;
|
self.tab_counter += 1;
|
||||||
self.tabs[0] =
|
self.tabs[0] = crate::app::document::DocumentTab::new_drawing(self.tab_counter);
|
||||||
crate::app::document::DocumentTab::new_drawing(self.tab_counter);
|
|
||||||
self.active_tab = 0;
|
self.active_tab = 0;
|
||||||
self.apply_display_defaults(0);
|
self.apply_display_defaults(0);
|
||||||
} else {
|
} else {
|
||||||
@@ -1892,7 +2039,8 @@ mod tests {
|
|||||||
acadrust::types::Color::Index(3),
|
acadrust::types::Color::Index(3),
|
||||||
);
|
);
|
||||||
let _ = app.on_fill_transparency(acadrust::types::Transparency::from_percent(0.35));
|
let _ = app.on_fill_transparency(acadrust::types::Transparency::from_percent(0.35));
|
||||||
assert!((app.tabs[i]
|
assert!(
|
||||||
|
(app.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.document
|
.document
|
||||||
.get_entity(selected_hatch)
|
.get_entity(selected_hatch)
|
||||||
@@ -1902,7 +2050,8 @@ mod tests {
|
|||||||
.as_percent()
|
.as_percent()
|
||||||
- 0.35)
|
- 0.35)
|
||||||
.abs()
|
.abs()
|
||||||
< 0.01);
|
< 0.01
|
||||||
|
);
|
||||||
let _ = app.on_fill_transparency(acadrust::types::Transparency::BY_LAYER);
|
let _ = app.on_fill_transparency(acadrust::types::Transparency::BY_LAYER);
|
||||||
assert!(app.tabs[i]
|
assert!(app.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
@@ -1912,6 +2061,170 @@ mod tests {
|
|||||||
.common()
|
.common()
|
||||||
.transparency
|
.transparency
|
||||||
.is_by_layer());
|
.is_by_layer());
|
||||||
|
|
||||||
|
// Line controls ignore HATCH entities, while fill controls ignore
|
||||||
|
// ordinary geometry in the same mixed selection.
|
||||||
|
app.tabs[i].scene.select_entity(line, false);
|
||||||
|
let _ = app.on_line_color(acadrust::types::Color::Index(4));
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.color,
|
||||||
|
acadrust::types::Color::Index(4),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(selected_hatch)
|
||||||
|
.expect("solid hatch")
|
||||||
|
.common()
|
||||||
|
.color,
|
||||||
|
acadrust::types::Color::Index(3),
|
||||||
|
);
|
||||||
|
let _ = app.on_solid_fill_color(acadrust::types::Color::Index(5));
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(selected_hatch)
|
||||||
|
.expect("solid hatch")
|
||||||
|
.common()
|
||||||
|
.color,
|
||||||
|
acadrust::types::Color::Index(5),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.color,
|
||||||
|
acadrust::types::Color::Index(4),
|
||||||
|
);
|
||||||
|
let _ = app.on_line_linetype("ByBlock".to_string());
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.linetype,
|
||||||
|
"ByBlock",
|
||||||
|
);
|
||||||
|
let _ = app.on_line_lineweight(acadrust::types::LineWeight::Value(50));
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.line_weight,
|
||||||
|
acadrust::types::LineWeight::Value(50),
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(selected_hatch)
|
||||||
|
.expect("solid hatch")
|
||||||
|
.common()
|
||||||
|
.line_weight,
|
||||||
|
acadrust::types::LineWeight::Value(50),
|
||||||
|
);
|
||||||
|
let _ = app.update(Message::LineLinetypeScaleInput("2.5".to_string()));
|
||||||
|
assert!(
|
||||||
|
(app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.linetype_scale
|
||||||
|
- 2.5)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
(app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(selected_hatch)
|
||||||
|
.expect("solid hatch")
|
||||||
|
.common()
|
||||||
|
.linetype_scale
|
||||||
|
- 1.0)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON
|
||||||
|
);
|
||||||
|
let _ = app.on_line_transparency(acadrust::types::Transparency::from_percent(0.2));
|
||||||
|
assert!(
|
||||||
|
(app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.transparency
|
||||||
|
.as_percent()
|
||||||
|
- 0.2)
|
||||||
|
.abs()
|
||||||
|
< 0.01
|
||||||
|
);
|
||||||
|
assert!(app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(selected_hatch)
|
||||||
|
.expect("solid hatch")
|
||||||
|
.common()
|
||||||
|
.transparency
|
||||||
|
.is_by_layer());
|
||||||
|
let _ = app.on_fill_transparency(acadrust::types::Transparency::from_percent(0.4));
|
||||||
|
assert!(
|
||||||
|
(app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(line)
|
||||||
|
.expect("line")
|
||||||
|
.common()
|
||||||
|
.transparency
|
||||||
|
.as_percent()
|
||||||
|
- 0.2)
|
||||||
|
.abs()
|
||||||
|
< 0.01
|
||||||
|
);
|
||||||
|
let pattern_name = crate::scene::model::hatch_patterns::catalog()
|
||||||
|
.iter()
|
||||||
|
.find(|entry| {
|
||||||
|
matches!(
|
||||||
|
entry.gpu,
|
||||||
|
crate::scene::model::hatch_model::HatchPattern::Pattern(_)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.expect("pattern hatch")
|
||||||
|
.name
|
||||||
|
.clone();
|
||||||
|
let _ = app.update(Message::GraphicHatchPatternChanged(pattern_name.clone()));
|
||||||
|
let selected_pattern = match app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(selected_hatch)
|
||||||
|
.expect("pattern hatch")
|
||||||
|
{
|
||||||
|
EntityType::Hatch(hatch) => hatch.pattern.name.as_str(),
|
||||||
|
_ => panic!("fill target is not a hatch"),
|
||||||
|
};
|
||||||
|
assert_eq!(selected_pattern, pattern_name);
|
||||||
|
app.tabs[i].scene.deselect_all();
|
||||||
|
app.tabs[i].scene.select_entity(selected_hatch, false);
|
||||||
|
|
||||||
let _ = app.on_graphic_attribute(GraphicAttribute::Hatch);
|
let _ = app.on_graphic_attribute(GraphicAttribute::Hatch);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
crate::ui::window::graphic_attributes::current(
|
crate::ui::window::graphic_attributes::current(
|
||||||
@@ -1948,6 +2261,38 @@ mod tests {
|
|||||||
assert_eq!(app.tabs[i].history.undo_stack.len(), undo_before + 1);
|
assert_eq!(app.tabs[i].history.undo_stack.len(), undo_before + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn graphic_line_controls_update_creation_defaults_without_a_selection() {
|
||||||
|
let mut app = fresh();
|
||||||
|
let i = app.active_tab;
|
||||||
|
app.tabs[i].scene.deselect_all();
|
||||||
|
|
||||||
|
let _ = app.on_line_color(acadrust::types::Color::Index(2));
|
||||||
|
let _ = app.on_line_linetype("Continuous".to_string());
|
||||||
|
let _ = app.on_line_lineweight(acadrust::types::LineWeight::Value(35));
|
||||||
|
let _ = app.on_line_linetype_scale(2.5);
|
||||||
|
let _ = app.on_line_transparency(acadrust::types::Transparency::from_percent(0.3));
|
||||||
|
|
||||||
|
let header = &app.tabs[i].scene.document.header;
|
||||||
|
assert_eq!(
|
||||||
|
header.current_entity_color,
|
||||||
|
acadrust::types::Color::Index(2)
|
||||||
|
);
|
||||||
|
assert_eq!(header.current_linetype_name, "Continuous");
|
||||||
|
assert_eq!(header.current_line_weight, 35);
|
||||||
|
assert!((header.current_entity_linetype_scale - 2.5).abs() < f64::EPSILON);
|
||||||
|
assert!(
|
||||||
|
(app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.current_entity_transparency()
|
||||||
|
.as_percent()
|
||||||
|
- 0.3)
|
||||||
|
.abs()
|
||||||
|
< 0.01
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// A foreign document: the fresh scene's document plus one model-space LINE.
|
/// A foreign document: the fresh scene's document plus one model-space LINE.
|
||||||
fn foreign_doc(app: &OpenCADStudio) -> acadrust::CadDocument {
|
fn foreign_doc(app: &OpenCADStudio) -> acadrust::CadDocument {
|
||||||
let mut doc = app.tabs[app.active_tab].scene.document.clone();
|
let mut doc = app.tabs[app.active_tab].scene.document.clone();
|
||||||
@@ -2072,12 +2417,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The nested-INSERT's block name inside a defined block record.
|
/// The nested-INSERT's block name inside a defined block record.
|
||||||
fn nested_insert_target(
|
fn nested_insert_target(doc: &acadrust::CadDocument, block: &str) -> Option<String> {
|
||||||
doc: &acadrust::CadDocument,
|
|
||||||
block: &str,
|
|
||||||
) -> Option<String> {
|
|
||||||
let br = doc.block_records.get(block)?;
|
let br = doc.block_records.get(block)?;
|
||||||
br.entity_handles.iter().find_map(|h| match doc.get_entity(*h)? {
|
br.entity_handles
|
||||||
|
.iter()
|
||||||
|
.find_map(|h| match doc.get_entity(*h)? {
|
||||||
EntityType::Insert(ins) => Some(ins.block_name.clone()),
|
EntityType::Insert(ins) => Some(ins.block_name.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
|||||||
+199
-15
@@ -1180,19 +1180,23 @@ impl OpenCADStudio {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::ImageEmbedPick => {
|
Message::ImageEmbedPick => Task::perform(
|
||||||
Task::perform(crate::io::pick_embedded_image_file(), Message::ImageEmbedPickResult)
|
crate::io::pick_embedded_image_file(),
|
||||||
}
|
Message::ImageEmbedPickResult,
|
||||||
|
),
|
||||||
|
|
||||||
Message::ImageEmbedPickResult(Ok(image)) => {
|
Message::ImageEmbedPickResult(Ok(image)) => {
|
||||||
use crate::command::CadCommand;
|
use crate::command::CadCommand;
|
||||||
use crate::modules::draw::draw::raster_image::ImageCommand;
|
use crate::modules::draw::draw::raster_image::ImageCommand;
|
||||||
self.command_line.push_output(crate::tf!(
|
self.command_line.push_output(
|
||||||
|
crate::tf!(
|
||||||
"IMAGEEMBED \"{name}\": {w}×{h} px (embedded)",
|
"IMAGEEMBED \"{name}\": {w}×{h} px (embedded)",
|
||||||
name = image.name.as_str(),
|
name = image.name.as_str(),
|
||||||
w = image.pixel_width,
|
w = image.pixel_width,
|
||||||
h = image.pixel_height,
|
h = image.pixel_height,
|
||||||
).as_ref());
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
let cmd = ImageCommand::new_embedded(image);
|
let cmd = ImageCommand::new_embedded(image);
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
@@ -1279,10 +1283,13 @@ impl OpenCADStudio {
|
|||||||
.insert(crate::io::font_repo::font_key(name));
|
.insert(crate::io::font_repo::font_key(name));
|
||||||
}
|
}
|
||||||
for (name, path) in &pairs {
|
for (name, path) in &pairs {
|
||||||
self.command_line.push_output(crate::tf!(
|
self.command_line.push_output(
|
||||||
|
crate::tf!(
|
||||||
"FONT Downloaded {name} → {path}",
|
"FONT Downloaded {name} → {path}",
|
||||||
path = path.display()
|
path = path.display()
|
||||||
).as_ref());
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if !unavailable.is_empty() {
|
if !unavailable.is_empty() {
|
||||||
self.command_line.push_info(crate::tf!(
|
self.command_line.push_info(crate::tf!(
|
||||||
@@ -3720,6 +3727,10 @@ impl OpenCADStudio {
|
|||||||
|
|
||||||
Message::ViewportLeftPress => {
|
Message::ViewportLeftPress => {
|
||||||
self.graphic_attribute_menu_open = false;
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
self.fill_transparency_menu_open = false;
|
self.fill_transparency_menu_open = false;
|
||||||
let sweep = self.sync_active_field_if_any();
|
let sweep = self.sync_active_field_if_any();
|
||||||
@@ -3730,6 +3741,10 @@ impl OpenCADStudio {
|
|||||||
|
|
||||||
Message::ViewportRightPress => {
|
Message::ViewportRightPress => {
|
||||||
self.graphic_attribute_menu_open = false;
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
self.fill_transparency_menu_open = false;
|
self.fill_transparency_menu_open = false;
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
@@ -3835,6 +3850,10 @@ impl OpenCADStudio {
|
|||||||
|
|
||||||
Message::ViewportMiddlePress => {
|
Message::ViewportMiddlePress => {
|
||||||
self.graphic_attribute_menu_open = false;
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
self.fill_transparency_menu_open = false;
|
self.fill_transparency_menu_open = false;
|
||||||
self.on_viewport_middle_press()
|
self.on_viewport_middle_press()
|
||||||
@@ -4993,13 +5012,21 @@ impl OpenCADStudio {
|
|||||||
}
|
}
|
||||||
if !state.objects_specify_onscreen && state.selected_handles.is_empty() {
|
if !state.objects_specify_onscreen && state.selected_handles.is_empty() {
|
||||||
state.error_message = Some(
|
state.error_message = Some(
|
||||||
crate::t!("No objects selected. You must select objects to define a block.")
|
crate::t!(
|
||||||
|
"No objects selected. You must select objects to define a block."
|
||||||
|
)
|
||||||
.into_owned(),
|
.into_owned(),
|
||||||
);
|
);
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
if self.tabs[i].scene.document.block_records.get(&name).is_some() {
|
if self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.block_records
|
||||||
|
.get(&name)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
state.confirm_redefine = Some(name);
|
state.confirm_redefine = Some(name);
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
@@ -9511,6 +9538,10 @@ impl OpenCADStudio {
|
|||||||
Message::BlockPalette(m) => self.on_block_palette(m),
|
Message::BlockPalette(m) => self.on_block_palette(m),
|
||||||
Message::ToggleGraphicAttributeDropdown => {
|
Message::ToggleGraphicAttributeDropdown => {
|
||||||
self.graphic_attribute_menu_open ^= true;
|
self.graphic_attribute_menu_open ^= true;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
self.fill_transparency_menu_open = false;
|
self.fill_transparency_menu_open = false;
|
||||||
Task::none()
|
Task::none()
|
||||||
@@ -9520,8 +9551,90 @@ impl OpenCADStudio {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::GraphicAttributeChanged(value) => self.on_graphic_attribute(value),
|
Message::GraphicAttributeChanged(value) => self.on_graphic_attribute(value),
|
||||||
|
Message::ToggleLineColorDropdown => {
|
||||||
|
self.line_color_menu_open ^= true;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.solid_fill_color_menu_open = false;
|
||||||
|
self.fill_transparency_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::CloseLineColorDropdown => {
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::LineColorChanged(color) => self.on_line_color(color),
|
||||||
|
Message::ToggleLineLinetypeMenu => {
|
||||||
|
self.line_linetype_menu_open ^= true;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.solid_fill_color_menu_open = false;
|
||||||
|
self.fill_transparency_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::CloseLineLinetypeMenu => {
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::LineLinetypeChanged(value) => self.on_line_linetype(value),
|
||||||
|
Message::ToggleLineLineweightMenu => {
|
||||||
|
self.line_lineweight_menu_open ^= true;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.solid_fill_color_menu_open = false;
|
||||||
|
self.fill_transparency_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::CloseLineLineweightMenu => {
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::LineLineweightChanged(value) => self.on_line_lineweight(value),
|
||||||
|
Message::LineLinetypeScaleChanged(value) => self.on_line_linetype_scale(value),
|
||||||
|
Message::LineLinetypeScaleInput(value) => {
|
||||||
|
let value = value.trim().replace(',', ".");
|
||||||
|
match value.parse::<f64>() {
|
||||||
|
Ok(value) if value.is_finite() && value > 0.0 => {
|
||||||
|
self.on_line_linetype_scale(value)
|
||||||
|
}
|
||||||
|
_ => Task::none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::ToggleLineTransparencyDropdown => {
|
||||||
|
self.line_transparency_menu_open ^= true;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.graphic_attribute_menu_open = false;
|
||||||
|
self.solid_fill_color_menu_open = false;
|
||||||
|
self.fill_transparency_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::CloseLineTransparencyDropdown => {
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::LineTransparencyChanged(value) => self.on_line_transparency(
|
||||||
|
acadrust::types::Transparency::from_percent(f64::from(value) / 100.0),
|
||||||
|
),
|
||||||
|
Message::LineTransparencyByLayer => {
|
||||||
|
self.on_line_transparency(acadrust::types::Transparency::BY_LAYER)
|
||||||
|
}
|
||||||
|
Message::LineTransparencyByBlock => {
|
||||||
|
self.on_line_transparency(acadrust::types::Transparency::BY_BLOCK)
|
||||||
|
}
|
||||||
Message::ToggleSolidFillColorDropdown => {
|
Message::ToggleSolidFillColorDropdown => {
|
||||||
self.solid_fill_color_menu_open ^= true;
|
self.solid_fill_color_menu_open ^= true;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.graphic_attribute_menu_open = false;
|
self.graphic_attribute_menu_open = false;
|
||||||
self.fill_transparency_menu_open = false;
|
self.fill_transparency_menu_open = false;
|
||||||
Task::none()
|
Task::none()
|
||||||
@@ -9533,6 +9646,10 @@ impl OpenCADStudio {
|
|||||||
Message::SolidFillColorChanged(color) => self.on_solid_fill_color(color),
|
Message::SolidFillColorChanged(color) => self.on_solid_fill_color(color),
|
||||||
Message::ToggleFillTransparencyDropdown => {
|
Message::ToggleFillTransparencyDropdown => {
|
||||||
self.fill_transparency_menu_open ^= true;
|
self.fill_transparency_menu_open ^= true;
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
self.line_linetype_menu_open = false;
|
||||||
|
self.line_lineweight_menu_open = false;
|
||||||
|
self.line_transparency_menu_open = false;
|
||||||
self.graphic_attribute_menu_open = false;
|
self.graphic_attribute_menu_open = false;
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
Task::none()
|
Task::none()
|
||||||
@@ -9552,17 +9669,78 @@ impl OpenCADStudio {
|
|||||||
}
|
}
|
||||||
Message::HatchEditorOpen => {
|
Message::HatchEditorOpen => {
|
||||||
self.gradient_editor = None;
|
self.gradient_editor = None;
|
||||||
self.hatch_editor_handles = Some(
|
let i = self.active_tab;
|
||||||
self.tabs[self.active_tab]
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
.scene
|
let handles = crate::ui::window::graphic_attributes::fill_handles(
|
||||||
.selected_handles_in_order(),
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
);
|
);
|
||||||
Task::none()
|
let current = handles.iter().find_map(|handle| {
|
||||||
|
let acadrust::EntityType::Hatch(hatch) =
|
||||||
|
self.tabs[i].scene.document.get_entity(*handle)?
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(hatch.pattern.name.as_str())
|
||||||
|
});
|
||||||
|
self.graphic_hatch_pattern_search.clear();
|
||||||
|
self.graphic_hatch_pattern_focus = current
|
||||||
|
.and_then(|current| {
|
||||||
|
crate::ui::properties::filtered_hatch_patterns("")
|
||||||
|
.iter()
|
||||||
|
.position(|entry| entry.name.eq_ignore_ascii_case(current))
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
self.hatch_editor_handles = Some(selected);
|
||||||
|
iced::widget::operation::focus(iced::widget::Id::new(
|
||||||
|
"graphic-hatch-pattern-search",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
Message::HatchEditorClose => {
|
Message::HatchEditorClose => {
|
||||||
self.hatch_editor_handles = None;
|
self.hatch_editor_handles = None;
|
||||||
|
self.graphic_hatch_pattern_search.clear();
|
||||||
|
self.graphic_hatch_pattern_focus = 0;
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
Message::GraphicHatchPatternSearchChanged(search) => {
|
||||||
|
self.graphic_hatch_pattern_search = search;
|
||||||
|
self.graphic_hatch_pattern_focus = 0;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::GraphicHatchPatternFocus(index) => {
|
||||||
|
let len = crate::ui::properties::filtered_hatch_patterns(
|
||||||
|
&self.graphic_hatch_pattern_search,
|
||||||
|
)
|
||||||
|
.len();
|
||||||
|
if index < len {
|
||||||
|
self.graphic_hatch_pattern_focus = index;
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::GraphicHatchPatternChanged(name) => {
|
||||||
|
let i = self.active_tab;
|
||||||
|
let selected = self.tabs[i].scene.selected_handles_in_order();
|
||||||
|
let handles = crate::ui::window::graphic_attributes::fill_handles(
|
||||||
|
&self.tabs[i].scene.document,
|
||||||
|
&selected,
|
||||||
|
);
|
||||||
|
self.hatch_editor_handles = None;
|
||||||
|
self.graphic_hatch_pattern_search.clear();
|
||||||
|
self.graphic_hatch_pattern_focus = 0;
|
||||||
|
self.apply_hatch_pattern(i, &handles, name)
|
||||||
|
}
|
||||||
|
Message::GraphicHatchPatternConfirm => {
|
||||||
|
let name = crate::ui::properties::filtered_hatch_patterns(
|
||||||
|
&self.graphic_hatch_pattern_search,
|
||||||
|
)
|
||||||
|
.get(self.graphic_hatch_pattern_focus)
|
||||||
|
.map(|entry| entry.name.clone());
|
||||||
|
if let Some(name) = name {
|
||||||
|
self.update(Message::GraphicHatchPatternChanged(name))
|
||||||
|
} else {
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
Message::GradientEditorOpen => {
|
Message::GradientEditorOpen => {
|
||||||
self.hatch_editor_handles = None;
|
self.hatch_editor_handles = None;
|
||||||
self.open_gradient_editor();
|
self.open_gradient_editor();
|
||||||
@@ -10777,8 +10955,14 @@ impl OpenCADStudio {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::OpenColorWindow(target, color) => {
|
Message::OpenColorWindow(target, color) => {
|
||||||
if matches!(target, crate::app::ColorPickTarget::GraphicAttributesSolid) {
|
match target {
|
||||||
|
crate::app::ColorPickTarget::GraphicAttributesLine => {
|
||||||
|
self.line_color_menu_open = false;
|
||||||
|
}
|
||||||
|
crate::app::ColorPickTarget::GraphicAttributesSolid => {
|
||||||
self.solid_fill_color_menu_open = false;
|
self.solid_fill_color_menu_open = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
self.color_pick_target = Some((target, color));
|
self.color_pick_target = Some((target, color));
|
||||||
|
|
||||||
|
|||||||
+22
-22
@@ -823,7 +823,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn on_mleader_style_edit(&mut self, field: &'static str, value: String) -> Task<Message> {
|
pub(super) fn on_mleader_style_edit(
|
||||||
|
&mut self, field: &'static str, value: String,) -> Task<Message> {
|
||||||
self.mls_color_open = None;
|
self.mls_color_open = None;
|
||||||
match field {
|
match field {
|
||||||
"landing_distance" => self.mls_landing_distance = value,
|
"landing_distance" => self.mls_landing_distance = value,
|
||||||
@@ -850,7 +851,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn on_mleader_style_set_enum(&mut self, field: &'static str, value: String) -> Task<Message> {
|
pub(super) fn on_mleader_style_set_enum(
|
||||||
|
&mut self, field: &'static str, value: String,) -> Task<Message> {
|
||||||
use acadrust::objects::{
|
use acadrust::objects::{
|
||||||
BlockContentConnectionType, LeaderContentType, LeaderDrawOrderType,
|
BlockContentConnectionType, LeaderContentType, LeaderDrawOrderType,
|
||||||
MultiLeaderDrawOrderType, MultiLeaderPathType, TextAlignmentType,
|
MultiLeaderDrawOrderType, MultiLeaderPathType, TextAlignmentType,
|
||||||
@@ -870,9 +872,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
|||||||
"BottomOfTopLineUnderlineTopLine" => {
|
"BottomOfTopLineUnderlineTopLine" => {
|
||||||
TextAttachmentType::BottomOfTopLineUnderlineTopLine
|
TextAttachmentType::BottomOfTopLineUnderlineTopLine
|
||||||
}
|
}
|
||||||
"BottomOfTopLineUnderlineAll" => {
|
"BottomOfTopLineUnderlineAll" =>
|
||||||
TextAttachmentType::BottomOfTopLineUnderlineAll
|
TextAttachmentType::BottomOfTopLineUnderlineAll,
|
||||||
}
|
|
||||||
"CenterOfText" => TextAttachmentType::CenterOfText,
|
"CenterOfText" => TextAttachmentType::CenterOfText,
|
||||||
"CenterOfTextOverline" => TextAttachmentType::CenterOfTextOverline,
|
"CenterOfTextOverline" => TextAttachmentType::CenterOfTextOverline,
|
||||||
_ => TextAttachmentType::MiddleOfTopLine,
|
_ => TextAttachmentType::MiddleOfTopLine,
|
||||||
@@ -898,9 +899,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
|||||||
}
|
}
|
||||||
"text_angle_type" => {
|
"text_angle_type" => {
|
||||||
s.text_angle_type = match value.as_str() {
|
s.text_angle_type = match value.as_str() {
|
||||||
"ParallelToLastLeaderLine" => {
|
"ParallelToLastLeaderLine" =>
|
||||||
TextAngleType::ParallelToLastLeaderLine
|
TextAngleType::ParallelToLastLeaderLine,
|
||||||
}
|
|
||||||
"Optimized" => TextAngleType::Optimized,
|
"Optimized" => TextAngleType::Optimized,
|
||||||
_ => TextAngleType::Horizontal,
|
_ => TextAngleType::Horizontal,
|
||||||
};
|
};
|
||||||
@@ -946,7 +946,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn on_mleader_style_set_handle(&mut self, field: &'static str, value: String) -> Task<Message> {
|
pub(super) fn on_mleader_style_set_handle(
|
||||||
|
&mut self, field: &'static str, value: String,) -> Task<Message> {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let doc = &self.tabs[i].scene.document;
|
let doc = &self.tabs[i].scene.document;
|
||||||
let handle: Option<acadrust::types::Handle> = if value == "None"
|
let handle: Option<acadrust::types::Handle> = if value == "None"
|
||||||
@@ -1136,35 +1137,34 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
|||||||
Some(crate::app::ColorPickTarget::Gradient(index)) => {
|
Some(crate::app::ColorPickTarget::Gradient(index)) => {
|
||||||
Some(Message::GradientColorChanged(index, color))
|
Some(Message::GradientColorChanged(index, color))
|
||||||
}
|
}
|
||||||
Some(crate::app::ColorPickTarget::GraphicAttributesSolid) => {
|
Some(crate::app::ColorPickTarget::GraphicAttributesLine) => {
|
||||||
|
Some(Message::LineColorChanged(color))
|
||||||
|
}
|
||||||
|
Some(crate::app::ColorPickTarget::GraphicAttributesSolid) => {
|
||||||
Some(Message::SolidFillColorChanged(color))
|
Some(Message::SolidFillColorChanged(color))
|
||||||
}
|
}
|
||||||
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 })
|
||||||
}
|
}
|
||||||
Some(crate::app::ColorPickTarget::Table(r, f)) => {
|
Some(crate::app::ColorPickTarget::Table(r, f)) =>
|
||||||
Some(Message::TableStyleCellEdit {
|
Some(Message::TableStyleCellEdit {
|
||||||
row: r,
|
row: r,
|
||||||
field: f,
|
field: f,
|
||||||
value: s,
|
value: s,
|
||||||
})
|
}),
|
||||||
}
|
Some(crate::app::ColorPickTarget::Properties) =>
|
||||||
Some(crate::app::ColorPickTarget::Properties) => {
|
Some(Message::PropColorChanged(color)),
|
||||||
Some(Message::PropColorChanged(color))
|
|
||||||
}
|
|
||||||
Some(crate::app::ColorPickTarget::PropertiesBg) => {
|
Some(crate::app::ColorPickTarget::PropertiesBg) => {
|
||||||
Some(Message::PropBgColorChanged(color))
|
Some(Message::PropBgColorChanged(color))
|
||||||
}
|
}
|
||||||
Some(crate::app::ColorPickTarget::PropertiesField(field)) => {
|
Some(crate::app::ColorPickTarget::PropertiesField(field)) => {
|
||||||
Some(Message::PropColorFieldChanged { field, color })
|
Some(Message::PropColorFieldChanged { field, color })
|
||||||
}
|
}
|
||||||
Some(crate::app::ColorPickTarget::MText) => {
|
Some(crate::app::ColorPickTarget::MText) =>
|
||||||
Some(Message::MTextColorChanged(color))
|
Some(Message::MTextColorChanged(color)),
|
||||||
}
|
Some(crate::app::ColorPickTarget::Ribbon) =>
|
||||||
Some(crate::app::ColorPickTarget::Ribbon) => {
|
Some(Message::RibbonColorChanged(color)),
|
||||||
Some(Message::RibbonColorChanged(color))
|
|
||||||
}
|
|
||||||
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);
|
||||||
Some(Message::LayerColorSet(color))
|
Some(Message::LayerColorSet(color))
|
||||||
|
|||||||
@@ -2943,6 +2943,7 @@ impl OpenCADStudio {
|
|||||||
let draw_depth = tab.scene.draw_depth_map();
|
let draw_depth = tab.scene.draw_depth_map();
|
||||||
crate::ui::window::graphic_attributes::view(
|
crate::ui::window::graphic_attributes::view(
|
||||||
&tab.scene.document,
|
&tab.scene.document,
|
||||||
|
&tab.properties,
|
||||||
&tab.scene
|
&tab.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -2953,9 +2954,15 @@ impl OpenCADStudio {
|
|||||||
auto_collapse,
|
auto_collapse,
|
||||||
side,
|
side,
|
||||||
self.graphic_attribute_menu_open,
|
self.graphic_attribute_menu_open,
|
||||||
|
self.line_color_menu_open,
|
||||||
|
self.line_linetype_menu_open,
|
||||||
|
self.line_lineweight_menu_open,
|
||||||
|
self.line_transparency_menu_open,
|
||||||
self.solid_fill_color_menu_open,
|
self.solid_fill_color_menu_open,
|
||||||
self.fill_transparency_menu_open,
|
self.fill_transparency_menu_open,
|
||||||
self.hatch_editor_handles.is_some(),
|
self.hatch_editor_handles.is_some(),
|
||||||
|
&self.graphic_hatch_pattern_search,
|
||||||
|
self.graphic_hatch_pattern_focus,
|
||||||
self.gradient_editor.as_ref(),
|
self.gradient_editor.as_ref(),
|
||||||
matches!(
|
matches!(
|
||||||
self.color_pick_target.as_ref().map(|(target, _)| target),
|
self.color_pick_target.as_ref().map(|(target, _)| target),
|
||||||
|
|||||||
+14
-5
@@ -1269,10 +1269,12 @@ impl Scene {
|
|||||||
style.0 = crate::scene::view::render::adapt_to_bg(style.0, hatch_bg);
|
style.0 = crate::scene::view::render::adapt_to_bg(style.0, hatch_bg);
|
||||||
m.aci = style.4;
|
m.aci = style.4;
|
||||||
m.line_weight_px = style.3;
|
m.line_weight_px = style.3;
|
||||||
// A gradient's colour is its first stop (already baked into
|
// Gradient RGB comes from its own colour stops, while
|
||||||
// the cached model); only solid / pattern fills take the
|
// transparency remains an entity property shared by both
|
||||||
// entity's resolved colour.
|
// stops. Solid/pattern fills take the complete style.
|
||||||
if !matches!(m.pattern, model::hatch_model::HatchPattern::Gradient { .. }) {
|
if matches!(m.pattern, model::hatch_model::HatchPattern::Gradient { .. }) {
|
||||||
|
m.set_alpha(style.0[3]);
|
||||||
|
} else {
|
||||||
m.color = style.0;
|
m.color = style.0;
|
||||||
}
|
}
|
||||||
if let EntityType::Hatch(dxf) = e {
|
if let EntityType::Hatch(dxf) = e {
|
||||||
@@ -2055,7 +2057,14 @@ impl Scene {
|
|||||||
.colors
|
.colors
|
||||||
.get(i)
|
.get(i)
|
||||||
.and_then(|e| e.color.rgb())
|
.and_then(|e| e.color.rgb())
|
||||||
.map(|(r, g, b)| [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0])
|
.map(|(r, g, b)| {
|
||||||
|
[
|
||||||
|
r as f32 / 255.0,
|
||||||
|
g as f32 / 255.0,
|
||||||
|
b as f32 / 255.0,
|
||||||
|
color[3],
|
||||||
|
]
|
||||||
|
})
|
||||||
};
|
};
|
||||||
gradient_color1 = stop(0);
|
gradient_color1 = stop(0);
|
||||||
let color1 = gradient_color1.unwrap_or(color);
|
let color1 = gradient_color1.unwrap_or(color);
|
||||||
|
|||||||
@@ -4,6 +4,43 @@ use acadrust::types::{Vector2, Vector3};
|
|||||||
use cadkernel::geom2d::{refine_spline_boundary, triangulate_rings, Curve, NurbsCurve};
|
use cadkernel::geom2d::{refine_spline_boundary, triangulate_rings, Curve, NurbsCurve};
|
||||||
use cadkernel::tessellation::DEFAULT_ANGLE;
|
use cadkernel::tessellation::DEFAULT_ANGLE;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gradient_stops_keep_entity_transparency() {
|
||||||
|
let mut hatch = Hatch::new();
|
||||||
|
hatch.is_solid = true;
|
||||||
|
hatch
|
||||||
|
.paths
|
||||||
|
.push(rectangle([0.0, 0.0], [10.0, 10.0], [0.0; 2]));
|
||||||
|
hatch.gradient_color.enabled = true;
|
||||||
|
hatch.gradient_color.name = "LINEAR".into();
|
||||||
|
hatch.gradient_color.colors = vec![
|
||||||
|
acadrust::entities::hatch::GradientColorEntry {
|
||||||
|
value: 0.0,
|
||||||
|
color: acadrust::types::Color::Rgb { r: 255, g: 0, b: 0 },
|
||||||
|
},
|
||||||
|
acadrust::entities::hatch::GradientColorEntry {
|
||||||
|
value: 1.0,
|
||||||
|
color: acadrust::types::Color::Rgb { r: 0, g: 0, b: 255 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut model = Scene::hatch_model_from_dxf(&hatch, [0.2, 0.3, 0.4, 0.35]).unwrap();
|
||||||
|
assert!((model.color[3] - 0.35).abs() < f32::EPSILON);
|
||||||
|
let crate::scene::model::hatch_model::HatchPattern::Gradient { color2, .. } = &model.pattern
|
||||||
|
else {
|
||||||
|
panic!("gradient hatch must produce a gradient render pattern");
|
||||||
|
};
|
||||||
|
assert!((color2[3] - 0.35).abs() < f32::EPSILON);
|
||||||
|
|
||||||
|
model.set_alpha(0.6);
|
||||||
|
assert!((model.color[3] - 0.6).abs() < f32::EPSILON);
|
||||||
|
let crate::scene::model::hatch_model::HatchPattern::Gradient { color2, .. } = &model.pattern
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
assert!((color2[3] - 0.6).abs() < f32::EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
fn narrow(clearance: f64, offset: [f64; 2]) -> NurbsCurve {
|
fn narrow(clearance: f64, offset: [f64; 2]) -> NurbsCurve {
|
||||||
let line = |a: [f64; 2], b: [f64; 2]| {
|
let line = |a: [f64; 2], b: [f64; 2]| {
|
||||||
[
|
[
|
||||||
@@ -184,8 +221,7 @@ fn valid_samples_are_unchanged_and_invalid_samples_stay_rejected() {
|
|||||||
|p| p
|
|p| p
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
assert!(refine_spline_boundary(
|
assert!(refine_spline_boundary(&vec![[0.0; 2]; 513],
|
||||||
&vec![[0.0; 2]; 513],
|
|
||||||
|| panic!("size cap ignored"),
|
|| panic!("size cap ignored"),
|
||||||
|p| p
|
|p| p
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -272,6 +272,18 @@ pub struct HatchModel {
|
|||||||
pub draw_depth: f32,
|
pub draw_depth: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HatchModel {
|
||||||
|
/// Apply an entity's resolved opacity to every colour stop. Gradient stop
|
||||||
|
/// colours come from the HATCH payload, but transparency remains a common
|
||||||
|
/// entity property and must affect both ends equally.
|
||||||
|
pub(crate) fn set_alpha(&mut self, alpha: f32) {
|
||||||
|
self.color[3] = alpha;
|
||||||
|
if let HatchPattern::Gradient { color2, .. } = &mut self.pattern {
|
||||||
|
color2[3] = alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HatchModel {
|
impl HatchModel {
|
||||||
pub(crate) fn gradient_frame(
|
pub(crate) fn gradient_frame(
|
||||||
&self,
|
&self,
|
||||||
@@ -350,9 +362,8 @@ impl HatchModel {
|
|||||||
self.pattern_segments_with_dot_length(Some(0.0))
|
self.pattern_segments_with_dot_length(Some(0.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_segments_with_dot_length(
|
fn pattern_segments_with_dot_length(&self,
|
||||||
&self,
|
plot_dot_length: Option<f64>
|
||||||
plot_dot_length: Option<f64>,
|
|
||||||
) -> Vec<[[f64; 2]; 2]> {
|
) -> Vec<[[f64; 2]; 2]> {
|
||||||
let HatchPattern::Pattern(families) = &self.pattern else {
|
let HatchPattern::Pattern(families) = &self.pattern else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
|||||||
+28
-22
@@ -2,8 +2,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub(super) fn paper_viewport_handles(
|
pub(super) fn paper_viewport_handles(&self
|
||||||
&self,
|
|
||||||
) -> (Handle, Handle, Arc<Vec<Handle>>) {
|
) -> (Handle, Handle, Arc<Vec<Handle>>) {
|
||||||
{
|
{
|
||||||
let cache = self.paper_viewport_cache.borrow();
|
let cache = self.paper_viewport_cache.borrow();
|
||||||
@@ -12,7 +11,7 @@ impl Scene {
|
|||||||
return (
|
return (
|
||||||
cache.layout_block,
|
cache.layout_block,
|
||||||
cache.sheet,
|
cache.sheet,
|
||||||
Arc::clone(&cache.content),
|
Arc::clone(&cache.content)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +122,8 @@ impl Scene {
|
|||||||
) -> Vec<ViewportInstance> {
|
) -> Vec<ViewportInstance> {
|
||||||
if self.current_layout == "Model" {
|
if self.current_layout == "Model" {
|
||||||
let tiles = self.model_tiles.borrow();
|
let tiles = self.model_tiles.borrow();
|
||||||
let active = self.active_model_tile.get().min(tiles.len().saturating_sub(1));
|
let active = self.active_model_tile.get()
|
||||||
|
.min(tiles.len().saturating_sub(1));
|
||||||
return tiles
|
return tiles
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -298,9 +298,7 @@ impl Scene {
|
|||||||
hatches.push(sheet);
|
hatches.push(sheet);
|
||||||
}
|
}
|
||||||
hatches.extend(
|
hatches.extend(
|
||||||
self.paper_canvas_hatches(tint_selected)
|
self.paper_canvas_hatches(tint_selected).iter().cloned()
|
||||||
.iter()
|
|
||||||
.cloned(),
|
|
||||||
);
|
);
|
||||||
let hatches = Arc::new(hatches);
|
let hatches = Arc::new(hatches);
|
||||||
let wipeouts = self.paper_canvas_wipeouts();
|
let wipeouts = self.paper_canvas_wipeouts();
|
||||||
@@ -377,9 +375,8 @@ impl Scene {
|
|||||||
|
|
||||||
/// Physical sheet bounds in canvas pixels. Uses the same forced top-down
|
/// Physical sheet bounds in canvas pixels. Uses the same forced top-down
|
||||||
/// paper transform as the GPU sheet viewport, ignoring stored camera twist.
|
/// paper transform as the GPU sheet viewport, ignoring stored camera twist.
|
||||||
pub fn paper_sheet_screen_rect(
|
pub fn paper_sheet_screen_rect(&self,
|
||||||
&self,
|
canvas_px: (f32, f32)
|
||||||
canvas_px: (f32, f32),
|
|
||||||
) -> Option<iced::Rectangle> {
|
) -> Option<iced::Rectangle> {
|
||||||
let ((x0, y0), (x1, y1)) = self.paper_limits()?;
|
let ((x0, y0), (x1, y1)) = self.paper_limits()?;
|
||||||
let (canvas_w, canvas_h) = canvas_px;
|
let (canvas_w, canvas_h) = canvas_px;
|
||||||
@@ -477,13 +474,17 @@ impl Scene {
|
|||||||
)
|
)
|
||||||
.is_some() =>
|
.is_some() =>
|
||||||
{
|
{
|
||||||
Self::hatch_model_from_dxf(dxf, model.color)
|
Self::hatch_model_from_dxf(dxf, model.color).unwrap_or_else(|| model.clone())
|
||||||
.unwrap_or_else(|| model.clone())
|
|
||||||
}
|
}
|
||||||
_ => model.clone(),
|
_ => model.clone(),
|
||||||
};
|
};
|
||||||
let style = self.render_style(entity);
|
let style = self.render_style(entity);
|
||||||
m.color = style.0;
|
if matches!(
|
||||||
|
m.pattern, model::hatch_model::HatchPattern::Gradient { .. }) {
|
||||||
|
m.set_alpha(style.0[3]);
|
||||||
|
} else {
|
||||||
|
m.color = style.0;
|
||||||
|
}
|
||||||
m.aci = style.4;
|
m.aci = style.4;
|
||||||
m.line_weight_px = style.3;
|
m.line_weight_px = style.3;
|
||||||
if let EntityType::Hatch(dxf) = entity {
|
if let EntityType::Hatch(dxf) = entity {
|
||||||
@@ -600,13 +601,19 @@ impl Scene {
|
|||||||
)
|
)
|
||||||
.is_some() =>
|
.is_some() =>
|
||||||
{
|
{
|
||||||
Self::hatch_model_from_dxf(dxf, model.color)
|
Self::hatch_model_from_dxf(dxf, model.color).unwrap_or_else(|| model.clone())
|
||||||
.unwrap_or_else(|| model.clone())
|
|
||||||
}
|
}
|
||||||
_ => model.clone(),
|
_ => model.clone(),
|
||||||
};
|
};
|
||||||
let style = self.render_style(entity);
|
let style = self.render_style(entity);
|
||||||
hatch.color = style.0;
|
if matches!(
|
||||||
|
hatch.pattern,
|
||||||
|
model::hatch_model::HatchPattern::Gradient { .. }
|
||||||
|
) {
|
||||||
|
hatch.set_alpha(style.0[3]);
|
||||||
|
} else {
|
||||||
|
hatch.color = style.0;
|
||||||
|
}
|
||||||
hatch.aci = style.4;
|
hatch.aci = style.4;
|
||||||
hatch.line_weight_px = style.3;
|
hatch.line_weight_px = style.3;
|
||||||
if let EntityType::Hatch(dxf) = entity {
|
if let EntityType::Hatch(dxf) = entity {
|
||||||
@@ -715,9 +722,9 @@ impl Scene {
|
|||||||
// from a big symbol library) has viewports legitimately aimed at that
|
// from a big symbol library) has viewports legitimately aimed at that
|
||||||
// second cluster; testing only the dense one wrongly auto-fits them onto
|
// second cluster; testing only the dense one wrongly auto-fits them onto
|
||||||
// the library. Fall back to the cluster box when no extents are known.
|
// the library. Fall back to the cluster box when no extents are known.
|
||||||
let full_bounds = self.model_space_extents().map(|(mn, mx)| {
|
let full_bounds = self.model_space_extents()
|
||||||
(mn.x as f64, mn.y as f64, mx.x as f64, mx.y as f64)
|
.map(|(mn, mx)|
|
||||||
});
|
(mn.x as f64, mn.y as f64, mx.x as f64, mx.y as f64));
|
||||||
// Absolute drawing centre. Geometry now reaches the scene at absolute
|
// Absolute drawing centre. Geometry now reaches the scene at absolute
|
||||||
// (UTM) coordinates — the old code centred the overlap test and the
|
// (UTM) coordinates — the old code centred the overlap test and the
|
||||||
// auto-fit on the origin, which was right only while world_offset
|
// auto-fit on the origin, which was right only while world_offset
|
||||||
@@ -734,9 +741,8 @@ impl Scene {
|
|||||||
(self.local_center[0], self.local_center[1])
|
(self.local_center[0], self.local_center[1])
|
||||||
} else {
|
} else {
|
||||||
self.model_space_extents()
|
self.model_space_extents()
|
||||||
.map(|(mn, mx)| {
|
.map(|(mn, mx)|
|
||||||
(((mn.x + mx.x) * 0.5) as f64, ((mn.y + mx.y) * 0.5) as f64)
|
(((mn.x + mx.x) * 0.5) as f64, ((mn.y + mx.y) * 0.5) as f64))
|
||||||
})
|
|
||||||
.unwrap_or((0.0, 0.0))
|
.unwrap_or((0.0, 0.0))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+163
-142
@@ -10,6 +10,7 @@
|
|||||||
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
||||||
use std::{fmt, sync::Arc};
|
use std::{fmt, sync::Arc};
|
||||||
|
|
||||||
|
use crate::t;
|
||||||
use crate::ui::ROW_H;
|
use crate::ui::ROW_H;
|
||||||
use acadrust::types::{Color as AcadColor, LineWeight};
|
use acadrust::types::{Color as AcadColor, LineWeight};
|
||||||
use acadrust::Handle;
|
use acadrust::Handle;
|
||||||
@@ -20,7 +21,6 @@ use iced::widget::{
|
|||||||
use iced::{
|
use iced::{
|
||||||
mouse, Background, Border, Color, Element, Length, Padding, Point, Rectangle, Size, Theme,
|
mouse, Background, Border, Color, Element, Length, Padding, Point, Rectangle, Size, Theme,
|
||||||
};
|
};
|
||||||
use crate::t;
|
|
||||||
|
|
||||||
// ── Row-height-derived constants ─────────────────────────────────────────
|
// ── Row-height-derived constants ─────────────────────────────────────────
|
||||||
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px
|
const FONT_SZ: f32 = ROW_H * 0.42; // ≈11 px
|
||||||
@@ -234,7 +234,8 @@ fn hatch_pattern_matches(
|
|||||||
let query = search.trim();
|
let query = search.trim();
|
||||||
query.is_empty()
|
query.is_empty()
|
||||||
|| entry.name.to_lowercase().contains(&query.to_lowercase())
|
|| entry.name.to_lowercase().contains(&query.to_lowercase())
|
||||||
|| entry.description.to_lowercase().contains(&query.to_lowercase())
|
|| entry.description.to_lowercase()
|
||||||
|
.contains(&query.to_lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn filtered_hatch_patterns(
|
pub(crate) fn filtered_hatch_patterns(
|
||||||
@@ -246,6 +247,100 @@ pub(crate) fn filtered_hatch_patterns(
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hatch_pattern_picker_content<'a>(
|
||||||
|
search_value: &'a str,
|
||||||
|
focus: usize,
|
||||||
|
current: &'a str,
|
||||||
|
search_id: &'static str,
|
||||||
|
on_search: fn(String) -> Message,
|
||||||
|
on_confirm: Message,
|
||||||
|
on_focus: fn(usize) -> Message,
|
||||||
|
on_changed: fn(String) -> Message,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
let search = text_input(t!("Search patterns…").as_ref(), search_value)
|
||||||
|
.id(iced::widget::Id::new(search_id))
|
||||||
|
.on_input(on_search)
|
||||||
|
.on_submit(on_confirm)
|
||||||
|
.size(FONT_SZ)
|
||||||
|
.padding([5, 7])
|
||||||
|
.width(Length::Fill);
|
||||||
|
|
||||||
|
let mut grid = column![].spacing(6);
|
||||||
|
let visible = filtered_hatch_patterns(search_value);
|
||||||
|
for (row_index, pair) in visible.chunks(2).enumerate() {
|
||||||
|
let mut cards = row![].spacing(6);
|
||||||
|
for (column_index, entry) in pair.iter().enumerate() {
|
||||||
|
let index = row_index * 2 + column_index;
|
||||||
|
let selected = current.eq_ignore_ascii_case(&entry.name);
|
||||||
|
let focused = focus == index;
|
||||||
|
let name = entry.name.clone();
|
||||||
|
let preview = hatch_pattern_preview(entry.gpu.clone(), PATTERN_PREVIEW_H);
|
||||||
|
let card = button(
|
||||||
|
column![
|
||||||
|
preview,
|
||||||
|
container(text(crate::ui::text_util::elide(&entry.name, 20)).size(FONT_SZ))
|
||||||
|
.width(Length::Fill)
|
||||||
|
.align_x(iced::Center),
|
||||||
|
]
|
||||||
|
.spacing(3),
|
||||||
|
)
|
||||||
|
.on_press(on_changed(name))
|
||||||
|
.style(move |theme: &Theme, status| {
|
||||||
|
let palette = theme.palette();
|
||||||
|
let hovered = matches!(status, button::Status::Hovered | button::Status::Pressed);
|
||||||
|
let pair = if selected {
|
||||||
|
palette.primary.weak
|
||||||
|
} else if hovered || focused {
|
||||||
|
palette.background.strong
|
||||||
|
} else {
|
||||||
|
palette.background.weak
|
||||||
|
};
|
||||||
|
button::Style {
|
||||||
|
background: Some(Background::Color(pair.color)),
|
||||||
|
text_color: pair.text,
|
||||||
|
border: Border {
|
||||||
|
color: if selected || focused {
|
||||||
|
palette.primary.base.color
|
||||||
|
} else {
|
||||||
|
palette.background.neutral.color
|
||||||
|
},
|
||||||
|
width: if selected || focused { 2.0 } else { 1.0 },
|
||||||
|
radius: 4.0.into(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.padding(5)
|
||||||
|
.width(PATTERN_CARD_W);
|
||||||
|
cards = cards.push(mouse_area(card).on_enter(on_focus(index)));
|
||||||
|
}
|
||||||
|
grid = grid.push(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
let results: Element<'_, Message> = if visible.is_empty() {
|
||||||
|
container(
|
||||||
|
text(t!("No matching patterns"))
|
||||||
|
.size(FONT_SZ)
|
||||||
|
.style(hint_text_style),
|
||||||
|
)
|
||||||
|
.padding(12)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.center_x(Length::Fill)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
scrollable(grid)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
container(column![search, results].spacing(7))
|
||||||
|
.style(container::bordered_box)
|
||||||
|
.padding(8)
|
||||||
|
.width(PATTERN_PICKER_W)
|
||||||
|
.height(Length::Fixed(PATTERN_PICKER_H))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// All standard CAD lineweight options for the combobox.
|
/// All standard CAD lineweight options for the combobox.
|
||||||
pub fn lw_options() -> Vec<LwItem> {
|
pub fn lw_options() -> Vec<LwItem> {
|
||||||
[
|
[
|
||||||
@@ -314,7 +409,7 @@ impl FieldKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Text-input widget id for one column of a named-parameter row.
|
/// Text-input widget id for one column of a named-parameter row.
|
||||||
pub fn prop_param_field_id(index: usize, field: crate::ui::window::named_parameters::ParamField) -> iced::widget::Id {
|
pub fn prop_param_field_id(index: usize, field: crate::ui::window::named_parameters::ParamField,) -> iced::widget::Id {
|
||||||
use crate::ui::window::named_parameters::ParamField;
|
use crate::ui::window::named_parameters::ParamField;
|
||||||
let suffix = match field {
|
let suffix = match field {
|
||||||
ParamField::Name => "name",
|
ParamField::Name => "name",
|
||||||
@@ -363,7 +458,7 @@ pub fn active_key_focused(
|
|||||||
/// its field key in O(1) instead of re-scanning the sections, enabling
|
/// its field key in O(1) instead of re-scanning the sections, enabling
|
||||||
/// select-all-on-focus for both text inputs and edit-choices (e.g. transparency).
|
/// select-all-on-focus for both text inputs and edit-choices (e.g. transparency).
|
||||||
pub fn build_field_key_map(
|
pub fn build_field_key_map(
|
||||||
sections: &[PropSection],
|
sections: &[PropSection]
|
||||||
) -> HashMap<iced::widget::Id, FieldKey> {
|
) -> HashMap<iced::widget::Id, FieldKey> {
|
||||||
let mut map = HashMap::default();
|
let mut map = HashMap::default();
|
||||||
for section in sections {
|
for section in sections {
|
||||||
@@ -577,7 +672,7 @@ impl PropertiesPanel {
|
|||||||
crate::ui::icons::themed_secondary(crate::ui::icons::PIN, 12.0)
|
crate::ui::icons::themed_secondary(crate::ui::icons::PIN, 12.0)
|
||||||
};
|
};
|
||||||
let pin = button(pin_icon)
|
let pin = button(pin_icon)
|
||||||
.on_press(Message::Dock(DockMsg::AutoCollapseToggle(PanelId::Properties)))
|
.on_press(Message::Dock(DockMsg::AutoCollapseToggle(PanelId::Properties,)))
|
||||||
.style(move |theme: &Theme, status| {
|
.style(move |theme: &Theme, status| {
|
||||||
let mut style = button::subtle(theme, status);
|
let mut style = button::subtle(theme, status);
|
||||||
if auto_collapse {
|
if auto_collapse {
|
||||||
@@ -602,9 +697,7 @@ impl PropertiesPanel {
|
|||||||
let close = tooltip(
|
let close = tooltip(
|
||||||
close,
|
close,
|
||||||
text(t!("Close")).size(10),
|
text(t!("Close")).size(10),
|
||||||
tooltip::Position::Bottom,
|
tooltip::Position::Bottom).gap(4);
|
||||||
)
|
|
||||||
.gap(4);
|
|
||||||
|
|
||||||
let header = mouse_area(
|
let header = mouse_area(
|
||||||
container(
|
container(
|
||||||
@@ -619,7 +712,7 @@ impl PropertiesPanel {
|
|||||||
)
|
)
|
||||||
.style(|theme: &Theme| container::Style {
|
.style(|theme: &Theme| container::Style {
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(
|
||||||
theme.palette().background.weak.color,
|
theme.palette().background.weak.color
|
||||||
)),
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
@@ -786,9 +879,7 @@ impl PropertiesPanel {
|
|||||||
let hdr = container(
|
let hdr = container(
|
||||||
row![
|
row![
|
||||||
title,
|
title,
|
||||||
toggle,
|
toggle,].align_y(iced::Center)
|
||||||
]
|
|
||||||
.align_y(iced::Center),
|
|
||||||
)
|
)
|
||||||
.style(|theme: &Theme| {
|
.style(|theme: &Theme| {
|
||||||
let palette = theme.palette();
|
let palette = theme.palette();
|
||||||
@@ -852,9 +943,8 @@ impl PropertiesPanel {
|
|||||||
label: &'a str,
|
label: &'a str,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
match &prop.value {
|
match &prop.value {
|
||||||
PropValue::ColorChoice(color) => {
|
PropValue::ColorChoice(color) =>
|
||||||
self.render_color_row(label, prop.field, *color, None)
|
self.render_color_row(label, prop.field, *color, None),
|
||||||
}
|
|
||||||
PropValue::NamedColorChoice { color, name } => {
|
PropValue::NamedColorChoice { color, name } => {
|
||||||
self.render_color_row(label, prop.field, *color, Some(name))
|
self.render_color_row(label, prop.field, *color, Some(name))
|
||||||
}
|
}
|
||||||
@@ -864,9 +954,8 @@ impl PropertiesPanel {
|
|||||||
PropValue::FieldLwChoice { field, value } => {
|
PropValue::FieldLwChoice { field, value } => {
|
||||||
self.render_field_lw_row(label, field, *value)
|
self.render_field_lw_row(label, field, *value)
|
||||||
}
|
}
|
||||||
PropValue::FieldLwVaries { field } => {
|
PropValue::FieldLwVaries { field } =>
|
||||||
self.render_field_lw_varies_row(label, field)
|
self.render_field_lw_varies_row(label, field),
|
||||||
}
|
|
||||||
PropValue::LwVaries => self.render_lw_varies_row(label),
|
PropValue::LwVaries => self.render_lw_varies_row(label),
|
||||||
PropValue::LinetypeChoice(lt) => self.render_linetype_row(label, lt),
|
PropValue::LinetypeChoice(lt) => self.render_linetype_row(label, lt),
|
||||||
PropValue::Choice { selected, options } => {
|
PropValue::Choice { selected, options } => {
|
||||||
@@ -888,16 +977,13 @@ impl PropertiesPanel {
|
|||||||
PropValue::ReadOnlyWithTooltip { value, tooltip } => {
|
PropValue::ReadOnlyWithTooltip { value, tooltip } => {
|
||||||
render_ro_with_tooltip_row(label, value, tooltip)
|
render_ro_with_tooltip_row(label, value, tooltip)
|
||||||
}
|
}
|
||||||
PropValue::HatchPatternChoice(current) => {
|
PropValue::HatchPatternChoice(current) =>
|
||||||
self.render_hatch_pattern_row(label, current)
|
self.render_hatch_pattern_row(label, current),
|
||||||
}
|
|
||||||
PropValue::AttrText { tag, value } => self.render_attr_row(tag, value),
|
PropValue::AttrText { tag, value } => self.render_attr_row(tag, value),
|
||||||
PropValue::EntityLink { id, handles, conflicting } => {
|
PropValue::EntityLink { id, handles, conflicting, } =>
|
||||||
render_entity_link_row(label, *id, handles.clone(), *conflicting)
|
render_entity_link_row(label, *id, handles.clone(), *conflicting),
|
||||||
}
|
PropValue::ParamRow { index, name, formula, resolved, } =>
|
||||||
PropValue::ParamRow { index, name, formula, resolved } => {
|
self.render_param_row(*index, name, formula, resolved),
|
||||||
self.render_param_row(*index, name, formula, resolved)
|
|
||||||
}
|
|
||||||
PropValue::ParamAddRow => render_param_add_row(),
|
PropValue::ParamAddRow => render_param_add_row(),
|
||||||
PropValue::ParamsVisibilityToggle(value) => render_params_visibility_toggle_row(*value),
|
PropValue::ParamsVisibilityToggle(value) => render_params_visibility_toggle_row(*value),
|
||||||
}
|
}
|
||||||
@@ -958,7 +1044,7 @@ impl PropertiesPanel {
|
|||||||
// (#415).
|
// (#415).
|
||||||
Message::OpenColorWindow(
|
Message::OpenColorWindow(
|
||||||
crate::app::ColorPickTarget::PropertiesBg,
|
crate::app::ColorPickTarget::PropertiesBg,
|
||||||
color,
|
color
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return prop_row_widget(label, selector);
|
return prop_row_widget(label, selector);
|
||||||
@@ -1034,7 +1120,7 @@ impl PropertiesPanel {
|
|||||||
Message::PropColorPickerToggle,
|
Message::PropColorPickerToggle,
|
||||||
Message::OpenColorWindow(
|
Message::OpenColorWindow(
|
||||||
crate::app::ColorPickTarget::Properties,
|
crate::app::ColorPickTarget::Properties,
|
||||||
color,
|
color
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
prop_row_widget(label, selector)
|
prop_row_widget(label, selector)
|
||||||
@@ -1052,7 +1138,7 @@ impl PropertiesPanel {
|
|||||||
Message::PropColorPickerToggle,
|
Message::PropColorPickerToggle,
|
||||||
Message::OpenColorWindow(
|
Message::OpenColorWindow(
|
||||||
crate::app::ColorPickTarget::Properties,
|
crate::app::ColorPickTarget::Properties,
|
||||||
AcadColor::ByLayer,
|
AcadColor::ByLayer
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
prop_row_widget(label, selector)
|
prop_row_widget(label, selector)
|
||||||
@@ -1442,24 +1528,28 @@ impl PropertiesPanel {
|
|||||||
use crate::ui::window::named_parameters::ParamField;
|
use crate::ui::window::named_parameters::ParamField;
|
||||||
|
|
||||||
let name_key = FieldKey::Param(index, ParamField::Name);
|
let name_key = FieldKey::Param(index, ParamField::Name);
|
||||||
let name_display = self.edit_buf.get(&name_key).map(|s| s.as_str()).unwrap_or(name);
|
let name_display = self.edit_buf.get(&name_key)
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or(name);
|
||||||
let name_active = self.active_field.as_ref() == Some(&name_key);
|
let name_active = self.active_field.as_ref() == Some(&name_key);
|
||||||
let name_input = text_input("name", name_display)
|
let name_input = text_input("name", name_display)
|
||||||
.id(name_key.widget_id())
|
.id(name_key.widget_id())
|
||||||
.on_input(move |v| Message::PropParamInput { index, field: ParamField::Name, value: v })
|
.on_input(move |v| Message::PropParamInput { index, field: ParamField::Name, value: v, })
|
||||||
.on_submit(Message::PropParamCommit { index, field: ParamField::Name })
|
.on_submit(Message::PropParamCommit { index, field: ParamField::Name, })
|
||||||
.size(FONT_SZ)
|
.size(FONT_SZ)
|
||||||
.style(text_input_style)
|
.style(text_input_style)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(Length::FillPortion(3));
|
.width(Length::FillPortion(3));
|
||||||
|
|
||||||
let formula_key = FieldKey::Param(index, ParamField::Formula);
|
let formula_key = FieldKey::Param(index, ParamField::Formula);
|
||||||
let formula_display = self.edit_buf.get(&formula_key).map(|s| s.as_str()).unwrap_or(formula);
|
let formula_display = self.edit_buf.get(&formula_key)
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or(formula);
|
||||||
let formula_active = self.active_field.as_ref() == Some(&formula_key);
|
let formula_active = self.active_field.as_ref() == Some(&formula_key);
|
||||||
let formula_input = text_input("formula", formula_display)
|
let formula_input = text_input("formula", formula_display)
|
||||||
.id(formula_key.widget_id())
|
.id(formula_key.widget_id())
|
||||||
.on_input(move |v| Message::PropParamInput { index, field: ParamField::Formula, value: v })
|
.on_input(move |v| Message::PropParamInput { index, field: ParamField::Formula, value: v, })
|
||||||
.on_submit(Message::PropParamCommit { index, field: ParamField::Formula })
|
.on_submit(Message::PropParamCommit { index, field: ParamField::Formula, })
|
||||||
.size(FONT_SZ)
|
.size(FONT_SZ)
|
||||||
.style(text_input_style)
|
.style(text_input_style)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
@@ -1470,9 +1560,10 @@ impl PropertiesPanel {
|
|||||||
Err(_) => ("—".to_string(), true),
|
Err(_) => ("—".to_string(), true),
|
||||||
};
|
};
|
||||||
let value_label = container(
|
let value_label = container(
|
||||||
text(value_text).size(FONT_SZ).style(move |theme: &Theme| iced::widget::text::Style {
|
text(value_text).size(FONT_SZ).style(move |theme: &Theme| { iced::widget::text::Style {
|
||||||
color: is_error.then_some(theme.palette().danger.base.color),
|
color: is_error.then_some(theme.palette().danger.base.color),
|
||||||
}),
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
.width(Length::FillPortion(2))
|
.width(Length::FillPortion(2))
|
||||||
.align_x(iced::Right);
|
.align_x(iced::Right);
|
||||||
@@ -1490,13 +1581,15 @@ impl PropertiesPanel {
|
|||||||
Background::Color(theme.palette().background.base.color)
|
Background::Color(theme.palette().background.base.color)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let content = container(row![name_input, formula_input, value_label, delete_btn].spacing(4).align_y(iced::Center))
|
let content = container(row![name_input, formula_input, value_label, delete_btn]
|
||||||
|
.spacing(4)
|
||||||
|
.align_y(iced::Center),)
|
||||||
.style(move |theme: &Theme| container::Style { background: Some(bg(theme)), ..Default::default() })
|
.style(move |theme: &Theme| container::Style { background: Some(bg(theme)), ..Default::default() })
|
||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
.width(Length::Fill);
|
.width(Length::Fill);
|
||||||
|
|
||||||
if let Err(err) = resolved {
|
if let Err(err) = resolved {
|
||||||
tooltip(content, text(err.as_str()).size(FONT_SZ), tooltip::Position::Top)
|
tooltip(content, text(err.as_str()).size(FONT_SZ), tooltip::Position::Top,)
|
||||||
.gap(4.0)
|
.gap(4.0)
|
||||||
.padding(6.0)
|
.padding(6.0)
|
||||||
.style(|theme: &Theme| {
|
.style(|theme: &Theme| {
|
||||||
@@ -1504,7 +1597,7 @@ impl PropertiesPanel {
|
|||||||
container::Style {
|
container::Style {
|
||||||
background: Some(Background::Color(palette.danger.weak.color)),
|
background: Some(Background::Color(palette.danger.weak.color)),
|
||||||
text_color: Some(palette.danger.weak.text),
|
text_color: Some(palette.danger.weak.text),
|
||||||
border: Border { color: palette.danger.base.color, width: 1.0, radius: 4.0.into() },
|
border: Border { color: palette.danger.base.color, width: 1.0, radius: 4.0.into(), },
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1562,96 +1655,16 @@ impl PropertiesPanel {
|
|||||||
return prop_row_widget(label, head.into());
|
return prop_row_widget(label, head.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let search = text_input(t!("Search patterns…").as_ref(), &self.hatch_pattern_search)
|
let popup = hatch_pattern_picker_content( &self.hatch_pattern_search,
|
||||||
.id(iced::widget::Id::new("hatch-pattern-search"))
|
self.hatch_pattern_focus,
|
||||||
.on_input(Message::PropHatchPatternSearchChanged)
|
current,"hatch-pattern-search",Message::PropHatchPatternSearchChanged,Message::PropHatchPatternConfirm,
|
||||||
.on_submit(Message::PropHatchPatternConfirm)
|
Message::PropHatchPatternFocus,
|
||||||
.size(FONT_SZ)
|
Message::PropHatchPatternChanged,
|
||||||
.padding([5, 7])
|
);
|
||||||
.width(Length::Fill);
|
|
||||||
|
|
||||||
let mut grid = column![].spacing(6);
|
|
||||||
let visible = filtered_hatch_patterns(&self.hatch_pattern_search);
|
|
||||||
for (row_index, pair) in visible.chunks(2).enumerate() {
|
|
||||||
let mut cards = row![].spacing(6);
|
|
||||||
for (column_index, entry) in pair.iter().enumerate() {
|
|
||||||
let index = row_index * 2 + column_index;
|
|
||||||
let selected = current.eq_ignore_ascii_case(&entry.name);
|
|
||||||
let focused = self.hatch_pattern_focus == index;
|
|
||||||
let name = entry.name.clone();
|
|
||||||
let preview = hatch_pattern_preview(entry.gpu.clone(), PATTERN_PREVIEW_H);
|
|
||||||
let card = button(
|
|
||||||
column![
|
|
||||||
preview,
|
|
||||||
container(text(crate::ui::text_util::elide(&entry.name, 20)).size(FONT_SZ))
|
|
||||||
.width(Length::Fill)
|
|
||||||
.align_x(iced::Center),
|
|
||||||
]
|
|
||||||
.spacing(3),
|
|
||||||
)
|
|
||||||
.on_press(Message::PropHatchPatternChanged(name))
|
|
||||||
.style(move |theme: &Theme, status| {
|
|
||||||
let palette = theme.palette();
|
|
||||||
let hovered =
|
|
||||||
matches!(status, button::Status::Hovered | button::Status::Pressed);
|
|
||||||
let pair = if selected {
|
|
||||||
palette.primary.weak
|
|
||||||
} else if hovered || focused {
|
|
||||||
palette.background.strong
|
|
||||||
} else {
|
|
||||||
palette.background.weak
|
|
||||||
};
|
|
||||||
button::Style {
|
|
||||||
background: Some(Background::Color(pair.color)),
|
|
||||||
text_color: pair.text,
|
|
||||||
border: Border {
|
|
||||||
color: if selected || focused {
|
|
||||||
palette.primary.base.color
|
|
||||||
} else {
|
|
||||||
palette.background.neutral.color
|
|
||||||
},
|
|
||||||
width: if selected || focused { 2.0 } else { 1.0 },
|
|
||||||
radius: 4.0.into(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.padding(5)
|
|
||||||
.width(PATTERN_CARD_W);
|
|
||||||
cards = cards.push(
|
|
||||||
mouse_area(card).on_enter(Message::PropHatchPatternFocus(index)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
grid = grid.push(cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
let results: Element<'_, Message> = if visible.is_empty() {
|
|
||||||
container(
|
|
||||||
text(t!("No matching patterns"))
|
|
||||||
.size(FONT_SZ)
|
|
||||||
.style(hint_text_style),
|
|
||||||
)
|
|
||||||
.padding(12)
|
|
||||||
.width(Length::Fill)
|
|
||||||
.center_x(Length::Fill)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
scrollable(grid)
|
|
||||||
.height(Length::Fill)
|
|
||||||
.width(Length::Fill)
|
|
||||||
.into()
|
|
||||||
};
|
|
||||||
let popup = container(column![search, results].spacing(7))
|
|
||||||
.style(container::bordered_box)
|
|
||||||
.padding(8)
|
|
||||||
.width(PATTERN_PICKER_W)
|
|
||||||
.height(Length::Fixed(PATTERN_PICKER_H));
|
|
||||||
|
|
||||||
prop_row_widget(
|
prop_row_widget(
|
||||||
label,
|
label,crate::ui::color_select::drop_down_below(
|
||||||
crate::ui::color_select::drop_down_below(
|
head.into(), popup,
|
||||||
head.into(),
|
|
||||||
popup.into(),
|
|
||||||
Some(Length::Fixed(PATTERN_PICKER_W)),
|
Some(Length::Fixed(PATTERN_PICKER_W)),
|
||||||
Length::Fixed(PATTERN_PICKER_H),
|
Length::Fixed(PATTERN_PICKER_H),
|
||||||
Message::PropHatchPatternPickerToggle(current.to_string()),
|
Message::PropHatchPatternPickerToggle(current.to_string()),
|
||||||
@@ -1707,11 +1720,10 @@ fn render_bool_row<'a>(label: &'a str, field: &'static str, value: bool) -> Elem
|
|||||||
};
|
};
|
||||||
let btn =
|
let btn =
|
||||||
button(
|
button(
|
||||||
text(btn_label)
|
text(btn_label).size(FONT_SZ).style(move |theme: &Theme| { iced::widget::text::Style {
|
||||||
.size(FONT_SZ)
|
|
||||||
.style(move |theme: &Theme| iced::widget::text::Style {
|
|
||||||
color: value.then_some(theme.palette().warning.base.color),
|
color: value.then_some(theme.palette().warning.base.color),
|
||||||
}),
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
.on_press(Message::PropBoolToggle(field))
|
.on_press(Message::PropBoolToggle(field))
|
||||||
.style(move |theme: &Theme, status| {
|
.style(move |theme: &Theme, status| {
|
||||||
@@ -1854,7 +1866,7 @@ fn render_group_row(
|
|||||||
let label_col = container(label_btn)
|
let label_col = container(label_btn)
|
||||||
.style(|theme: &Theme| container::Style {
|
.style(|theme: &Theme| container::Style {
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(
|
||||||
theme.palette().background.weakest.color,
|
theme.palette().background.weakest.color
|
||||||
)),
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
@@ -1867,7 +1879,7 @@ fn render_group_row(
|
|||||||
let value_col = container(value_field)
|
let value_col = container(value_field)
|
||||||
.style(|theme: &Theme| container::Style {
|
.style(|theme: &Theme| container::Style {
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(
|
||||||
theme.palette().background.base.color,
|
theme.palette().background.base.color
|
||||||
)),
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
@@ -1895,7 +1907,7 @@ fn render_group_row(
|
|||||||
}
|
}
|
||||||
fn render_annotative_scale_row<'a>(
|
fn render_annotative_scale_row<'a>(
|
||||||
label: &'a str,
|
label: &'a str,
|
||||||
value: &'a str,
|
value: &'a str
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let field = crate::ui::read_only::field(value, FONT_SZ, Length::Fill);
|
let field = crate::ui::read_only::field(value, FONT_SZ, Length::Fill);
|
||||||
|
|
||||||
@@ -1941,7 +1953,7 @@ fn render_ro_with_tooltip_row<'a>(
|
|||||||
tooltip_text: &'a str,
|
tooltip_text: &'a str,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let field = crate::ui::read_only::field(value, FONT_SZ, Length::Fill);
|
let field = crate::ui::read_only::field(value, FONT_SZ, Length::Fill);
|
||||||
let wrapped = tooltip(field, text(tooltip_text).size(FONT_SZ), tooltip::Position::Top)
|
let wrapped = tooltip(field, text(tooltip_text).size(FONT_SZ), tooltip::Position::Top,)
|
||||||
.gap(4.0)
|
.gap(4.0)
|
||||||
.padding(6.0)
|
.padding(6.0)
|
||||||
.style(|theme: &Theme| {
|
.style(|theme: &Theme| {
|
||||||
@@ -1989,7 +2001,7 @@ fn render_entity_link_row<'a>(
|
|||||||
};
|
};
|
||||||
button::Style {
|
button::Style {
|
||||||
background: Some(Background::Color(pair.color)),
|
background: Some(Background::Color(pair.color)),
|
||||||
border: Border { color: palette.background.neutral.color, width: 1.0, radius: 2.0.into() },
|
border: Border { color: palette.background.neutral.color, width: 1.0, radius: 2.0.into(), },
|
||||||
text_color: pair.text,
|
text_color: pair.text,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@@ -2012,7 +2024,10 @@ fn render_entity_link_row<'a>(
|
|||||||
/// — avoids a separate "pending new row" concept, since every row always
|
/// — avoids a separate "pending new row" concept, since every row always
|
||||||
/// reflects a real committed table entry.
|
/// reflects a real committed table entry.
|
||||||
fn render_param_add_row<'a>() -> Element<'a, Message> {
|
fn render_param_add_row<'a>() -> Element<'a, Message> {
|
||||||
let btn = button(row![text("+").size(FONT_SZ), text(t!("Add parameter").into_owned()).size(FONT_SZ)].spacing(6).align_y(iced::Center))
|
let btn = button(row![text("+").size(FONT_SZ), text(t!("Add parameter").into_owned()).size(FONT_SZ)
|
||||||
|
]
|
||||||
|
.spacing(6)
|
||||||
|
.align_y(iced::Center),)
|
||||||
.on_press(Message::PropParamAddNew)
|
.on_press(Message::PropParamAddNew)
|
||||||
.style(button::text)
|
.style(button::text)
|
||||||
.padding([4, 8])
|
.padding([4, 8])
|
||||||
@@ -2045,14 +2060,18 @@ fn render_params_visibility_toggle_row<'a>(value: bool) -> Element<'a, Message>
|
|||||||
};
|
};
|
||||||
button::Style {
|
button::Style {
|
||||||
background: Some(Background::Color(pair.color)),
|
background: Some(Background::Color(pair.color)),
|
||||||
border: Border { color: palette.background.neutral.color, width: 1.0, radius: 2.0.into() },
|
border: Border { color: palette.background.neutral.color, width: 1.0, radius: 2.0.into(), },
|
||||||
text_color: pair.text,
|
text_color: pair.text,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.padding([4, 8])
|
.padding([4, 8])
|
||||||
.width(Length::Fill);
|
.width(Length::Fill);
|
||||||
container(row![text(t!("Values").into_owned()).size(FONT_SZ).width(Length::Fill), btn].align_y(iced::Center))
|
container(row![text(t!("Values").into_owned())
|
||||||
|
.size(FONT_SZ)
|
||||||
|
.width(Length::Fill), btn
|
||||||
|
]
|
||||||
|
.align_y(iced::Center),)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -2202,6 +2221,8 @@ fn text_input_style(theme: &Theme, status: text_input::Status) -> text_input::St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate)
|
||||||
|
|
||||||
fn combo_input_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
|
fn combo_input_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
|
||||||
text_input_style(theme, status)
|
text_input_style(theme, status)
|
||||||
}
|
}
|
||||||
@@ -2226,7 +2247,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn sample_sections() -> Vec<crate::scene::model::object::PropSection> {
|
fn sample_sections() -> Vec<crate::scene::model::object::PropSection> {
|
||||||
use crate::scene::model::object::{Property, PropValue};
|
use crate::scene::model::object::{PropValue,Property};
|
||||||
vec![
|
vec![
|
||||||
crate::scene::model::object::PropSection {
|
crate::scene::model::object::PropSection {
|
||||||
title: "Geometry".into(),
|
title: "Geometry".into(),
|
||||||
|
|||||||
+772
-235
File diff suppressed because it is too large.
Load diff
@@ -1,36 +0,0 @@
|
|||||||
use crate::app::Message;
|
|
||||||
use iced::widget::{button, column, container, mouse_area, row, text, Space};
|
|
||||||
use iced::{Background, Border, Element, Fill, Length, Theme};
|
|
||||||
|
|
||||||
pub fn view() -> Element<'static, Message> {
|
|
||||||
let content = column![
|
|
||||||
text("Hatch settings").size(12),
|
|
||||||
text("Additional hatch settings will be available here.").size(11),
|
|
||||||
Space::new().height(12),
|
|
||||||
container(
|
|
||||||
row![button(text(crate::t!("Close")).size(11))
|
|
||||||
.on_press(Message::HatchEditorClose)
|
|
||||||
.style(button::secondary)
|
|
||||||
.padding([5, 14])]
|
|
||||||
.width(Fill),
|
|
||||||
)
|
|
||||||
.width(Fill)
|
|
||||||
.align_x(iced::alignment::Horizontal::Right),
|
|
||||||
]
|
|
||||||
.spacing(8)
|
|
||||||
.padding(10);
|
|
||||||
let panel = container(content)
|
|
||||||
.width(Length::Fixed(390.0))
|
|
||||||
.style(|theme: &Theme| container::Style {
|
|
||||||
background: Some(Background::Color(theme.palette().background.base.color)),
|
|
||||||
border: Border {
|
|
||||||
color: theme.palette().primary.base.color,
|
|
||||||
width: 1.0,
|
|
||||||
radius: 3.0.into(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
mouse_area(panel)
|
|
||||||
.interaction(iced::mouse::Interaction::Pointer)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
+22
-23
@@ -1,33 +1,32 @@
|
|||||||
pub mod about;
|
pub mod about;
|
||||||
|
pub mod alias_editor;
|
||||||
|
pub mod annotation_data;
|
||||||
|
pub mod attribute_editor;
|
||||||
|
pub mod auto_constrain_settings;
|
||||||
pub mod block_definition;
|
pub mod block_definition;
|
||||||
pub mod block_palette;
|
pub mod block_palette;
|
||||||
pub mod browser;
|
pub mod browser;
|
||||||
pub mod graphic_attributes;
|
|
||||||
pub mod gradient_editor;
|
|
||||||
pub mod hatch_editor;
|
|
||||||
pub mod layout_manager;
|
|
||||||
pub mod layer_state_manager;
|
|
||||||
pub mod drawing_units;
|
|
||||||
pub mod geometric_tolerance;
|
|
||||||
pub mod drafting_settings;
|
pub mod drafting_settings;
|
||||||
pub mod auto_constrain_settings;
|
pub mod drawing_units;
|
||||||
pub mod layer_translator;
|
|
||||||
pub mod plot;
|
|
||||||
pub mod print_all;
|
|
||||||
pub mod plugin_manager;
|
|
||||||
pub mod shortcuts;
|
|
||||||
pub mod layers;
|
|
||||||
pub mod update_notice;
|
|
||||||
pub mod open_progress;
|
|
||||||
pub mod missing_fonts;
|
|
||||||
pub mod recovery;
|
|
||||||
pub mod options;
|
|
||||||
pub mod attribute_editor;
|
|
||||||
pub mod annotation_data;
|
|
||||||
pub mod alias_editor;
|
|
||||||
pub mod find_replace;
|
pub mod find_replace;
|
||||||
|
pub mod geometric_tolerance;
|
||||||
|
pub mod gradient_editor;
|
||||||
|
pub mod graphic_attributes;
|
||||||
|
pub mod layer_state_manager;
|
||||||
|
pub mod layer_translator;
|
||||||
|
pub mod layers;
|
||||||
|
pub mod layout_manager;
|
||||||
|
pub mod missing_fonts;
|
||||||
pub mod named_parameters;
|
pub mod named_parameters;
|
||||||
|
pub mod open_progress;
|
||||||
|
pub mod options;
|
||||||
|
pub mod plot;
|
||||||
|
pub mod plugin_manager;
|
||||||
|
pub mod print_all;
|
||||||
|
pub mod recovery;
|
||||||
|
pub mod shortcuts;
|
||||||
|
pub mod update_notice;
|
||||||
|
pub mod wblock;
|
||||||
pub mod xref_attach;
|
pub mod xref_attach;
|
||||||
pub mod xref_help;
|
pub mod xref_help;
|
||||||
pub mod xref_manager;
|
pub mod xref_manager;
|
||||||
pub mod wblock;
|
|
||||||
Reference in new issue
Block a user