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
@@ -770,6 +770,12 @@ pub(super) struct OpenCADStudio {
|
||||
show_properties: bool,
|
||||
show_graphic_attributes: 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,
|
||||
fill_transparency_menu_open: bool,
|
||||
gradient_editor: Option<crate::ui::window::gradient_editor::GradientEditorState>,
|
||||
@@ -1531,6 +1537,7 @@ pub enum ColorPickerTab {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ColorPickTarget {
|
||||
Gradient(u8),
|
||||
GraphicAttributesLine,
|
||||
GraphicAttributesSolid,
|
||||
DimStyle(DsField),
|
||||
MLeader(&'static str),
|
||||
@@ -2057,6 +2064,22 @@ pub enum Message {
|
||||
ToggleGraphicAttributeDropdown,
|
||||
CloseGraphicAttributeDropdown,
|
||||
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,
|
||||
CloseSolidFillColorDropdown,
|
||||
SolidFillColorChanged(AcadColor),
|
||||
@@ -2067,6 +2090,10 @@ pub enum Message {
|
||||
FillTransparencyByBlock,
|
||||
HatchEditorOpen,
|
||||
HatchEditorClose,
|
||||
GraphicHatchPatternSearchChanged(String),
|
||||
GraphicHatchPatternFocus(usize),
|
||||
GraphicHatchPatternChanged(String),
|
||||
GraphicHatchPatternConfirm,
|
||||
GradientEditorOpen,
|
||||
GradientColorModeChanged(crate::ui::window::gradient_editor::GradientColorMode),
|
||||
GradientColorToggle(u8),
|
||||
@@ -4130,6 +4157,12 @@ impl OpenCADStudio {
|
||||
show_properties: true,
|
||||
show_graphic_attributes: true,
|
||||
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,
|
||||
fill_transparency_menu_open: false,
|
||||
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;
|
||||
}
|
||||
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!(
|
||||
entry.gpu,
|
||||
crate::scene::model::hatch_model::HatchPattern::Pattern(_)
|
||||
)
|
||||
}) else {
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
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);
|
||||
let origin = hatch.pattern_origin();
|
||||
crate::entities::hatch::translate_pattern_geometry(
|
||||
&mut pattern,
|
||||
origin.x,
|
||||
origin.y,
|
||||
);
|
||||
crate::entities::hatch::translate_pattern_geometry(&mut pattern, origin.x, origin.y);
|
||||
hatch.pattern = pattern;
|
||||
hatch.pattern_type = acadrust::entities::HatchPatternType::Predefined;
|
||||
hatch.is_solid = false;
|
||||
@@ -200,10 +199,16 @@ impl OpenCADStudio {
|
||||
use crate::scene::model::hatch_model::{GradientKind, HatchPattern};
|
||||
use crate::ui::window::graphic_attributes::GraphicAttribute;
|
||||
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.fill_transparency_menu_open = false;
|
||||
self.gradient_editor = None;
|
||||
self.hatch_editor_handles = None;
|
||||
self.graphic_hatch_pattern_search.clear();
|
||||
self.graphic_hatch_pattern_focus = 0;
|
||||
if value == GraphicAttribute::Varies {
|
||||
return iced::Task::none();
|
||||
}
|
||||
@@ -220,8 +225,8 @@ impl OpenCADStudio {
|
||||
return iced::Task::none();
|
||||
}
|
||||
let selected_handles: rustc_hash::FxHashSet<_> = selected.iter().copied().collect();
|
||||
let (associated, new_hatches, updated_hatches, skipped) =
|
||||
if value == GraphicAttribute::None {
|
||||
let (associated, new_hatches, updated_hatches, skipped) = if value == GraphicAttribute::None
|
||||
{
|
||||
let associated: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
@@ -294,7 +299,8 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.document.get_entity(*handle)
|
||||
{
|
||||
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,
|
||||
&hatch.common,
|
||||
None,
|
||||
@@ -402,7 +408,7 @@ impl OpenCADStudio {
|
||||
self.solid_fill_color_menu_open = false;
|
||||
let i = self.active_tab;
|
||||
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,
|
||||
&selected,
|
||||
);
|
||||
@@ -410,7 +416,7 @@ impl OpenCADStudio {
|
||||
.into_iter()
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.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) {
|
||||
crate::scene::view::dispatch::apply_color(entity, color);
|
||||
}
|
||||
@@ -418,6 +424,146 @@ impl OpenCADStudio {
|
||||
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(
|
||||
&mut self,
|
||||
transparency: acadrust::types::Transparency,
|
||||
@@ -520,8 +666,11 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEvent) -> Task<Message> {
|
||||
pub(super) fn on_ribbon_tool_click(
|
||||
&mut self,
|
||||
tool_id: String,
|
||||
event: ModuleEvent,
|
||||
) -> Task<Message> {
|
||||
// Commands use `start_allowed`; other events need a drawing
|
||||
// and stay blocked on the Start page (#299, #388, #389).
|
||||
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;
|
||||
self.tabs[i].scene.clear();
|
||||
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) => {
|
||||
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 => {
|
||||
// Name the styles that do exist, from the same
|
||||
// list every other caller reads.
|
||||
self.command_line.push_error(
|
||||
crate::tf!("Unknown visual style \"{name}\".").as_ref(),
|
||||
);
|
||||
self.command_line
|
||||
.push_error(crate::tf!("Unknown visual style \"{name}\".").as_ref());
|
||||
self.command_line
|
||||
.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(
|
||||
async move {
|
||||
let exts: Vec<&str> =
|
||||
extensions.iter().map(|s| s.as_str()).collect();
|
||||
let exts: Vec<&str> = extensions.iter().map(|s| s.as_str()).collect();
|
||||
let path = crate::sys::file_dialog()
|
||||
.set_title(title)
|
||||
.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));
|
||||
if self.tabs.len() == 1 {
|
||||
self.tab_counter += 1;
|
||||
self.tabs[0] =
|
||||
crate::app::document::DocumentTab::new_drawing(self.tab_counter);
|
||||
self.tabs[0] = crate::app::document::DocumentTab::new_drawing(self.tab_counter);
|
||||
self.active_tab = 0;
|
||||
self.apply_display_defaults(0);
|
||||
} else {
|
||||
@@ -1892,7 +2039,8 @@ mod tests {
|
||||
acadrust::types::Color::Index(3),
|
||||
);
|
||||
let _ = app.on_fill_transparency(acadrust::types::Transparency::from_percent(0.35));
|
||||
assert!((app.tabs[i]
|
||||
assert!(
|
||||
(app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(selected_hatch)
|
||||
@@ -1902,7 +2050,8 @@ mod tests {
|
||||
.as_percent()
|
||||
- 0.35)
|
||||
.abs()
|
||||
< 0.01);
|
||||
< 0.01
|
||||
);
|
||||
let _ = app.on_fill_transparency(acadrust::types::Transparency::BY_LAYER);
|
||||
assert!(app.tabs[i]
|
||||
.scene
|
||||
@@ -1912,6 +2061,170 @@ mod tests {
|
||||
.common()
|
||||
.transparency
|
||||
.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);
|
||||
assert_eq!(
|
||||
crate::ui::window::graphic_attributes::current(
|
||||
@@ -1948,6 +2261,38 @@ mod tests {
|
||||
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.
|
||||
fn foreign_doc(app: &OpenCADStudio) -> acadrust::CadDocument {
|
||||
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.
|
||||
fn nested_insert_target(
|
||||
doc: &acadrust::CadDocument,
|
||||
block: &str,
|
||||
) -> Option<String> {
|
||||
fn nested_insert_target(doc: &acadrust::CadDocument, block: &str) -> Option<String> {
|
||||
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()),
|
||||
_ => None,
|
||||
})
|
||||
|
||||
+199
-15
@@ -1180,19 +1180,23 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::ImageEmbedPick => {
|
||||
Task::perform(crate::io::pick_embedded_image_file(), Message::ImageEmbedPickResult)
|
||||
}
|
||||
Message::ImageEmbedPick => Task::perform(
|
||||
crate::io::pick_embedded_image_file(),
|
||||
Message::ImageEmbedPickResult,
|
||||
),
|
||||
|
||||
Message::ImageEmbedPickResult(Ok(image)) => {
|
||||
use crate::command::CadCommand;
|
||||
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)",
|
||||
name = image.name.as_str(),
|
||||
w = image.pixel_width,
|
||||
h = image.pixel_height,
|
||||
).as_ref());
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
let cmd = ImageCommand::new_embedded(image);
|
||||
let i = self.active_tab;
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
@@ -1279,10 +1283,13 @@ impl OpenCADStudio {
|
||||
.insert(crate::io::font_repo::font_key(name));
|
||||
}
|
||||
for (name, path) in &pairs {
|
||||
self.command_line.push_output(crate::tf!(
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"FONT Downloaded {name} → {path}",
|
||||
path = path.display()
|
||||
).as_ref());
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
if !unavailable.is_empty() {
|
||||
self.command_line.push_info(crate::tf!(
|
||||
@@ -3720,6 +3727,10 @@ impl OpenCADStudio {
|
||||
|
||||
Message::ViewportLeftPress => {
|
||||
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.fill_transparency_menu_open = false;
|
||||
let sweep = self.sync_active_field_if_any();
|
||||
@@ -3730,6 +3741,10 @@ impl OpenCADStudio {
|
||||
|
||||
Message::ViewportRightPress => {
|
||||
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.fill_transparency_menu_open = false;
|
||||
let i = self.active_tab;
|
||||
@@ -3835,6 +3850,10 @@ impl OpenCADStudio {
|
||||
|
||||
Message::ViewportMiddlePress => {
|
||||
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.fill_transparency_menu_open = false;
|
||||
self.on_viewport_middle_press()
|
||||
@@ -4993,13 +5012,21 @@ impl OpenCADStudio {
|
||||
}
|
||||
if !state.objects_specify_onscreen && state.selected_handles.is_empty() {
|
||||
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(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
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);
|
||||
return Task::none();
|
||||
}
|
||||
@@ -9511,6 +9538,10 @@ impl OpenCADStudio {
|
||||
Message::BlockPalette(m) => self.on_block_palette(m),
|
||||
Message::ToggleGraphicAttributeDropdown => {
|
||||
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.fill_transparency_menu_open = false;
|
||||
Task::none()
|
||||
@@ -9520,8 +9551,90 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
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 => {
|
||||
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.fill_transparency_menu_open = false;
|
||||
Task::none()
|
||||
@@ -9533,6 +9646,10 @@ impl OpenCADStudio {
|
||||
Message::SolidFillColorChanged(color) => self.on_solid_fill_color(color),
|
||||
Message::ToggleFillTransparencyDropdown => {
|
||||
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.solid_fill_color_menu_open = false;
|
||||
Task::none()
|
||||
@@ -9552,17 +9669,78 @@ impl OpenCADStudio {
|
||||
}
|
||||
Message::HatchEditorOpen => {
|
||||
self.gradient_editor = None;
|
||||
self.hatch_editor_handles = Some(
|
||||
self.tabs[self.active_tab]
|
||||
.scene
|
||||
.selected_handles_in_order(),
|
||||
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,
|
||||
);
|
||||
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 => {
|
||||
self.hatch_editor_handles = None;
|
||||
self.graphic_hatch_pattern_search.clear();
|
||||
self.graphic_hatch_pattern_focus = 0;
|
||||
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 => {
|
||||
self.hatch_editor_handles = None;
|
||||
self.open_gradient_editor();
|
||||
@@ -10777,8 +10955,14 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
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.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()
|
||||
}
|
||||
|
||||
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;
|
||||
match field {
|
||||
"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()
|
||||
}
|
||||
|
||||
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::{
|
||||
BlockContentConnectionType, LeaderContentType, LeaderDrawOrderType,
|
||||
MultiLeaderDrawOrderType, MultiLeaderPathType, TextAlignmentType,
|
||||
@@ -870,9 +872,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
||||
"BottomOfTopLineUnderlineTopLine" => {
|
||||
TextAttachmentType::BottomOfTopLineUnderlineTopLine
|
||||
}
|
||||
"BottomOfTopLineUnderlineAll" => {
|
||||
TextAttachmentType::BottomOfTopLineUnderlineAll
|
||||
}
|
||||
"BottomOfTopLineUnderlineAll" =>
|
||||
TextAttachmentType::BottomOfTopLineUnderlineAll,
|
||||
"CenterOfText" => TextAttachmentType::CenterOfText,
|
||||
"CenterOfTextOverline" => TextAttachmentType::CenterOfTextOverline,
|
||||
_ => TextAttachmentType::MiddleOfTopLine,
|
||||
@@ -898,9 +899,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
||||
}
|
||||
"text_angle_type" => {
|
||||
s.text_angle_type = match value.as_str() {
|
||||
"ParallelToLastLeaderLine" => {
|
||||
TextAngleType::ParallelToLastLeaderLine
|
||||
}
|
||||
"ParallelToLastLeaderLine" =>
|
||||
TextAngleType::ParallelToLastLeaderLine,
|
||||
"Optimized" => TextAngleType::Optimized,
|
||||
_ => TextAngleType::Horizontal,
|
||||
};
|
||||
@@ -946,7 +946,8 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
|
||||
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 doc = &self.tabs[i].scene.document;
|
||||
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(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(crate::app::ColorPickTarget::DimStyle(f)) => Some(Message::DsEdit(f, s)),
|
||||
Some(crate::app::ColorPickTarget::MLeader(f)) => {
|
||||
Some(Message::MLeaderStyleEdit { field: f, value: s })
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::Table(r, f)) => {
|
||||
Some(crate::app::ColorPickTarget::Table(r, f)) =>
|
||||
Some(Message::TableStyleCellEdit {
|
||||
row: r,
|
||||
field: f,
|
||||
value: s,
|
||||
})
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::Properties) => {
|
||||
Some(Message::PropColorChanged(color))
|
||||
}
|
||||
}),
|
||||
Some(crate::app::ColorPickTarget::Properties) =>
|
||||
Some(Message::PropColorChanged(color)),
|
||||
Some(crate::app::ColorPickTarget::PropertiesBg) => {
|
||||
Some(Message::PropBgColorChanged(color))
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::PropertiesField(field)) => {
|
||||
Some(Message::PropColorFieldChanged { field, color })
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::MText) => {
|
||||
Some(Message::MTextColorChanged(color))
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::Ribbon) => {
|
||||
Some(Message::RibbonColorChanged(color))
|
||||
}
|
||||
Some(crate::app::ColorPickTarget::MText) =>
|
||||
Some(Message::MTextColorChanged(color)),
|
||||
Some(crate::app::ColorPickTarget::Ribbon) =>
|
||||
Some(Message::RibbonColorChanged(color)),
|
||||
Some(crate::app::ColorPickTarget::Layer(idx)) => {
|
||||
self.tabs[self.active_tab].layers.selected = Some(idx);
|
||||
Some(Message::LayerColorSet(color))
|
||||
|
||||
@@ -2943,6 +2943,7 @@ impl OpenCADStudio {
|
||||
let draw_depth = tab.scene.draw_depth_map();
|
||||
crate::ui::window::graphic_attributes::view(
|
||||
&tab.scene.document,
|
||||
&tab.properties,
|
||||
&tab.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
@@ -2953,9 +2954,15 @@ impl OpenCADStudio {
|
||||
auto_collapse,
|
||||
side,
|
||||
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.fill_transparency_menu_open,
|
||||
self.hatch_editor_handles.is_some(),
|
||||
&self.graphic_hatch_pattern_search,
|
||||
self.graphic_hatch_pattern_focus,
|
||||
self.gradient_editor.as_ref(),
|
||||
matches!(
|
||||
self.color_pick_target.as_ref().map(|(target, _)| target),
|
||||
|
||||
Reference in new issue
Block a user