Merge pull request #1722 from ramox81/codex/view-background

# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/lo-LA/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
This commit is contained in:
Hakan Seven committed 2026-10-13 07:44:01 +03:00
commit 03445a540d
40 files changed
+1801 -46

No files matched your search

+4 -1
View File
@@ -891,7 +891,8 @@ impl OpenCADStudio {
self.command_line
.push_error(crate::t!("Usage: VIEW DELETE <name>").as_ref());
} else {
if self.tabs[i].scene.document.views.remove(&name).is_some() {
if let Some(view) = self.tabs[i].scene.document.views.remove(&name) {
crate::scene::erase_background(&mut self.tabs[i].scene.document, view.background_handle);
self.command_line
.push_output(crate::tf!("View '{}' deleted.", name).as_ref());
} else {
@@ -909,6 +910,7 @@ impl OpenCADStudio {
let found = self.tabs[i].scene.document.views.get(&name).cloned();
if let Some(v) = found {
self.tabs[i].scene.restore_named_view(&v);
self.apply_view_render_mode(i, &v);
self.command_line.push_output(
crate::tf!("View '{}' restored.", v.name).as_ref(),
);
@@ -932,6 +934,7 @@ impl OpenCADStudio {
let found = self.tabs[i].scene.document.views.get(&name).cloned();
if let Some(v) = found {
self.tabs[i].scene.restore_named_view(&v);
self.apply_view_render_mode(i, &v);
self.command_line
.push_output(crate::tf!("View '{}' restored.", v.name).as_ref());
} else {
+11
View File
@@ -439,6 +439,7 @@ impl OpenCADStudio {
"attach" => crate::t!("Attaches a PDF file as an underlay."),
"pointcloud" => crate::t!("Attaches a point cloud scan or project."),
"pcsection" => crate::t!("Traces the points on the kept side of a live section as lines."),
"background" => std::borrow::Cow::Owned(crate::tr!("view-background", "help")),
"pointcloudcolormap" => {
crate::t!("Sets the color schemes and ranges a point cloud is colored by intensity or elevation.")
}
@@ -572,6 +573,16 @@ impl OpenCADStudio {
| PdfDialogMsg::MapCurrent(_)
| PdfDialogMsg::MapApply
| PdfDialogMsg::MapOk) => return self.update_point_cloud_color_map(message),
message @ (PdfDialogMsg::BgKind(_)
| PdfDialogMsg::BgPickColor(_)
| PdfDialogMsg::BgColor(..)
| PdfDialogMsg::BgThree(_)
| PdfDialogMsg::BgRotation(_)
| PdfDialogMsg::BgImage(_)
| PdfDialogMsg::BgBrowse
| PdfDialogMsg::BgPosition(_)
| PdfDialogMsg::BgAspect(_)
| PdfDialogMsg::BgApply) => return self.update_view_background(message),
message @ (PdfDialogMsg::SecPerimeter(_)
| PdfDialogMsg::SecMaxPoints(_)
| PdfDialogMsg::SecLayer(_)
+302 -4
View File
@@ -53,12 +53,33 @@ impl OpenCADStudio {
.to_string(),
visual_style: name_of(&visual_styles, view.visual_style_handle),
live_section: name_of(&live_sections, view.live_section_handle),
paper_space: view.paper_space,
})
.collect();
let scene = &self.tabs[i].scene;
let sheet_viewports = (scene.current_layout != "Model" && scene.active_viewport.is_none()).then(|| {
scene
.layout_content_viewports()
.iter()
.filter_map(|handle| match document.get_entity(*handle) {
Some(codec::EntityType::Viewport(value)) => Some((
handle.value(),
[
value.center.x - value.width / 2.0,
value.center.y - value.height / 2.0,
value.center.x + value.width / 2.0,
value.center.y + value.height / 2.0,
],
)),
_ => None,
})
.collect()
});
ViewNames {
views,
visual_styles: visual_styles.into_iter().map(|(name, _)| name).collect(),
live_sections: live_sections.into_iter().map(|(name, _)| name).collect(),
sheet_viewports,
}
}
@@ -111,7 +132,7 @@ impl OpenCADStudio {
}
/// Carry out what -VIEW gathered (`-VIEW >VERB args`).
pub(super) fn run_dash_view(&mut self, rest: &str, i: usize) -> Task<Message> {
pub(crate) fn run_dash_view(&mut self, rest: &str, i: usize) -> Task<Message> {
let (verb, arg) = rest.split_once(' ').unwrap_or((rest, ""));
let mut settings_again = false;
match verb {
@@ -132,18 +153,43 @@ impl OpenCADStudio {
if !doomed.is_empty() {
self.push_undo_snapshot(i, "-VIEW");
for name in &doomed {
self.tabs[i].scene.document.views.remove(name);
let removed = self.tabs[i].scene.document.views.remove(name);
if let Some(view) = removed {
crate::scene::erase_background(&mut self.tabs[i].scene.document, view.background_handle);
}
self.tabs[i].scene.document.delete_layer_state(&snapshot_name(name));
}
self.tabs[i].dirty = true;
}
}
"RESTOREVP" => {
let (name, handle) = arg.split_once('\u{1f}').unwrap_or((arg, ""));
let Some(view) = self.tabs[i].scene.document.views.get(name).cloned() else {
return Task::none();
};
let Some(handle) = u64::from_str_radix(handle, 16).ok().map(Handle::new) else {
return Task::none();
};
// The reference enters the selected viewport and restores the
// view in it.
let task = self.update(Message::EnterViewport(handle));
self.restore_view_in_active_viewport(i, &view);
return task;
}
"RESTORE" => {
let Some(view) = self.tabs[i].scene.document.views.get(arg).cloned() else {
return Task::none();
};
if self.tabs[i].scene.current_layout != "Model"
&& self.tabs[i].scene.active_viewport.is_some()
&& !view.paper_space
{
self.restore_view_in_active_viewport(i, &view);
return Task::none();
}
let before = self.tabs[i].scene.active_gaze_dir();
self.tabs[i].scene.restore_named_view(&view);
self.apply_view_render_mode(i, &view);
if view.ucs_associated && self.tabs[i].active_block_edit.is_none() {
let world = view.ucs_x_axis == Vector3::new(1.0, 0.0, 0.0)
&& view.ucs_y_axis == Vector3::new(0.0, 1.0, 0.0)
@@ -197,6 +243,14 @@ impl OpenCADStudio {
}
}
"SETTINGS" => settings_again = true,
// Background type chosen: its dialog takes over until Apply or Cancel.
"BACKGROUND" => {
if let Some((view, kind)) = arg.split_once('\u{1f}') {
self.open_view_background(i, view, kind);
return self.finish_dispatch("-VIEW");
}
settings_again = true;
}
"UCSVIEW" => {
view_cmd::set_ucs_view(arg == "1");
self.save_config();
@@ -241,18 +295,42 @@ impl OpenCADStudio {
}
}
/// A model-space view restored in the entered paper-space viewport, with
/// its background and visual style.
fn restore_view_in_active_viewport(&mut self, i: usize, view: &codec::tables::View) {
let Some(viewport) = self.tabs[i].scene.active_viewport else { return };
self.tabs[i].scene.restore_named_view_in_viewport(view, viewport);
if let Some(ObjectType::VisualStyle(style)) =
self.tabs[i].scene.document.objects.get(&view.visual_style_handle)
{
if let Some((mode, _)) = crate::modules::view::visual_style::mode_for_style_name(&style.description) {
self.tabs[i].scene.set_active_viewport_render_mode(mode);
self.tabs[i].scene.bump_geometry_no_blocks();
}
}
self.tabs[i].dirty = true;
}
/// Add or replace a named view, keeping the record (and so its settings)
/// of a view saved again under the same name.
fn save_named_view(&mut self, i: usize, mut view: codec::tables::View) {
self.push_undo_snapshot(i, "-VIEW");
let background = self.tabs[i].scene.active_background();
let document = &mut self.tabs[i].scene.document;
view.background_handle = crate::scene::copy_background(document, background);
let mut replaced = Handle::NULL;
match document.views.get(&view.name).cloned() {
Some(old) => {
view.name = old.name;
view.handle = old.handle;
view.visual_style_handle = old.visual_style_handle;
view.live_section_handle = old.live_section_handle;
view.background_handle = old.background_handle;
// The view keeps its background unless the viewport has
// one, which the view then takes a copy of.
if view.background_handle.is_null() {
view.background_handle = old.background_handle;
}
replaced = old.background_handle;
}
None => {
view.handle = document.allocate_handle();
@@ -265,6 +343,7 @@ impl OpenCADStudio {
}
}
document.views.add_or_replace(view);
crate::scene::erase_background(document, replaced);
self.tabs[i].dirty = true;
}
@@ -275,6 +354,7 @@ impl OpenCADStudio {
};
self.push_undo_snapshot(i, "-VIEW");
let document = &mut self.tabs[i].scene.document;
let mut replaced = Handle::NULL;
if view.handle.is_null() {
view.handle = document.allocate_handle();
}
@@ -282,7 +362,7 @@ impl OpenCADStudio {
list.into_iter().find(|(entry, _)| entry.eq_ignore_ascii_case(value)).map_or(Handle::NULL, |(_, h)| h)
};
match setting {
"BACKGROUND" => view.background_handle = Handle::NULL,
"BACKGROUND" => replaced = std::mem::replace(&mut view.background_handle, Handle::NULL),
"VISUALSTYLE" => view.visual_style_handle = find(crate::io::visual_styles::offered(document)),
"SECTION" => {
let sections: Vec<(String, Handle)> = document
@@ -336,6 +416,224 @@ impl OpenCADStudio {
_ => {}
}
document.views.add_or_replace(view);
crate::scene::erase_background(document, replaced);
self.tabs[i].dirty = true;
}
}
impl OpenCADStudio {
/// The Background dialog for a named view, on the type typed at the
/// prompt and filled from the view's background where it has one.
fn open_view_background(&mut self, i: usize, view: &str, kind: &str) {
use crate::ui::window::pdf_dialogs::{BgKind, BgPosition, ViewBackgroundState};
use codec::objects::ClassObjectData as D;
let unpack = |c: u32| [(c >> 16) as u8, (c >> 8) as u8, c as u8];
let canvas = self.tabs[i].scene.bg_color;
let mut state = ViewBackgroundState {
view: view.to_string(),
kind: match kind {
"SOLID" => BgKind::Solid,
"GRADIENT" => BgKind::Gradient,
"IMAGE" => BgKind::Image,
_ => BgKind::None,
},
// The reference's defaults.
solid: [128, 128, 255],
top: [204, 204, 204],
middle: [128, 128, 128],
bottom: [51, 51, 51],
three: false,
rotation: "0".into(),
image: String::new(),
position: BgPosition::Center,
aspect: false,
offset: [0.0, 0.0],
scale: [1.0, 1.0],
canvas: [0, 1, 2].map(|k| (canvas[k].clamp(0.0, 1.0) * 255.0).round() as u8),
};
let document = &self.tabs[i].scene.document;
let current = document.views.get(view).map_or(Handle::NULL, |v| v.background_handle);
if let Some(ObjectType::ClassObject(object)) = document.objects.get(&current) {
match &object.data {
D::SolidBackground(value) => state.solid = unpack(value.color),
D::GradientBackground(value) => {
(state.top, state.middle, state.bottom) = (
unpack(value.color_top),
unpack(value.color_middle),
unpack(value.color_bottom),
);
state.three = value.height > 0.0;
state.rotation = crate::ui::window::pdf_dialogs::short(value.rotation);
}
D::ImageBackground(value) => {
state.image = value.filename.clone();
state.position = if value.use_tiling {
BgPosition::Tile
} else if value.fit_to_screen {
BgPosition::Stretch
} else {
BgPosition::Center
};
state.aspect = value.maintain_aspect_ratio;
state.offset = [value.offset.x, value.offset.y];
state.scale = [value.scale.x, value.scale.y];
}
_ => {}
}
}
self.view_background = Some(state);
self.active_modal = Some(crate::app::ModalKind::ViewBackground);
}
pub(in crate::app) fn update_view_background(
&mut self,
message: crate::ui::window::pdf_dialogs::PdfDialogMsg,
) -> Task<Message> {
use crate::ui::window::pdf_dialogs::{BgKind, BgPosition, PdfDialogMsg as M};
use codec::objects::{
ClassObjectData as D, GradientBackground, ImageBackground, SolidBackground,
};
let i = self.active_tab;
let Some(state) = self.view_background.as_mut() else {
return Task::none();
};
match message {
M::BgKind(kind) => state.kind = kind,
M::BgThree(on) => state.three = on,
M::BgRotation(value) => state.rotation = value,
M::BgImage(value) => state.image = value,
M::BgPosition(value) => state.position = value,
M::BgAspect(on) => state.aspect = on,
M::BgColor(slot, rgb) => match slot {
0 => state.solid = rgb,
1 => state.top = rgb,
2 => state.middle = rgb,
_ => state.bottom = rgb,
},
M::BgPickColor(slot) => {
let [r, g, b] = match slot {
0 => state.solid,
1 => state.top,
2 => state.middle,
_ => state.bottom,
};
return Task::done(Message::OpenColorWindow(
crate::app::ColorPickTarget::ViewBackground(slot),
codec::types::Color::Rgb { r, g, b },
));
}
M::BgBrowse => {
return Task::perform(
async {
crate::sys::file_dialog()
.set_title(crate::tr!("view-background", "select-image"))
.add_filter(
crate::tr!("view-background", "image-files"),
&["png", "jpg", "jpeg", "bmp", "tif", "tiff", "gif", "tga"],
)
.pick_file()
.await
.map(|handle| crate::sys::handle_path(&handle))
},
|path| match path {
Some(path) => Message::PdfDialog(M::BgImage(path.display().to_string())),
None => Message::Noop,
},
);
}
M::BgApply => {
// Colours are stored as true colours (flag byte 0xC2), as the
// reference stores them.
let pack = |c: [u8; 3]| {
0xC200_0000 | (c[0] as u32) << 16 | (c[1] as u32) << 8 | c[2] as u32
};
let data = match state.kind {
BgKind::None => None,
BgKind::Solid => Some(D::SolidBackground(SolidBackground {
class_version: 1,
color: pack(state.solid),
})),
BgKind::Gradient => {
let Some(rotation) = state
.rotation
.trim()
.parse::<f64>()
.ok()
.filter(|v| v.is_finite())
else {
self.command_line
.push_error(crate::t!("Requires a number.").as_ref());
return Task::none();
};
Some(D::GradientBackground(GradientBackground {
class_version: 1,
color_top: pack(state.top),
color_middle: pack(state.middle),
color_bottom: pack(state.bottom),
horizon: 0.5,
// The reference marks a three-colour gradient by its
// middle band's height.
height: if state.three { 0.33 } else { 0.0 },
rotation,
}))
}
BgKind::Image => {
if !std::path::Path::new(state.image.trim()).is_file() {
self.command_line.push_error("Invalid Background Image File!");
return Task::none();
}
Some(D::ImageBackground(ImageBackground {
class_version: 1,
filename: state.image.trim().to_string(),
fit_to_screen: state.position == BgPosition::Stretch,
maintain_aspect_ratio: state.aspect,
use_tiling: state.position == BgPosition::Tile,
offset: codec::types::Vector2::new(state.offset[0], state.offset[1]),
scale: codec::types::Vector2::new(state.scale[0], state.scale[1]),
}))
}
};
let name = state.view.clone();
self.view_background = None;
self.close_active_modal();
if self.tabs[i].scene.document.views.get(&name).is_some() {
self.push_undo_snapshot(i, "-VIEW");
let document = &mut self.tabs[i].scene.document;
let handle = data.map_or(Handle::NULL, |data| {
crate::scene::add_background(document, data)
});
if let Some(view) = document.views.get_mut(&name) {
let old = std::mem::replace(&mut view.background_handle, handle);
crate::scene::erase_background(document, old);
}
self.tabs[i].dirty = true;
}
return self.run_dash_view("SETTINGS", i);
}
_ => {}
}
Task::none()
}
}
impl OpenCADStudio {
/// A restored view's visual style becomes the model tab's render mode
/// (the closest of OCS's modes); a view without one leaves it.
pub(crate) fn apply_view_render_mode(&mut self, i: usize, view: &codec::tables::View) {
if self.tabs[i].scene.current_layout != "Model" {
return;
}
let Some(ObjectType::VisualStyle(style)) =
self.tabs[i].scene.document.objects.get(&view.visual_style_handle)
else {
return;
};
use crate::modules::view::visual_style;
let Some((mode, named)) = visual_style::mode_for_style_name(&style.description) else {
return;
};
// A style of its own keeps its name; the render modes keep theirs.
let label = if named { style.description.clone() } else { visual_style::label_for(mode).to_string() };
self.apply_render_mode(i, mode, &label);
}
}
+48
View File
@@ -103,6 +103,7 @@ pub(super) const NAMES: &[&str] = &[
#[cfg(not(target_arch = "wasm32"))]
"external_change_cancel",
"pdf_dialog_ok",
"view_background",
"pdf_layer_toggle",
"pc_manager_toggle",
"pc_manager",
@@ -1786,6 +1787,53 @@ impl OpenCADStudio {
})
}
"ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()),
// The -VIEW Background dialog, one field per call: "type=gradient",
// "color|top|middle|bottom=r,g,b", "three=1", "rotation=30",
// "image=<path>", "position=center|stretch|tile", "aspect=1",
// "apply" or "cancel".
"view_background" => {
use crate::ui::window::pdf_dialogs::{BgKind, BgPosition, PdfDialogMsg as P};
if self.active_modal != Some(crate::app::ModalKind::ViewBackground) {
return Err(failure("no_dialog", "The Background dialog is not open"));
}
let spec = string(req, "value")?;
let (key, value) = spec.split_once('=').unwrap_or((spec, ""));
let bad = || failure("invalid_value", spec);
let rgb = || -> Result<[u8; 3], Value> {
let parts: Vec<u8> = value
.split(',')
.map(|part| part.trim().parse::<u8>())
.collect::<Result<_, _>>()
.map_err(|_| bad())?;
<[u8; 3]>::try_from(parts).map_err(|_| bad())
};
match key {
"cancel" => Message::CloseModal,
"apply" => Message::PdfDialog(P::BgApply),
"type" => Message::PdfDialog(P::BgKind(match value {
"none" => BgKind::None,
"solid" => BgKind::Solid,
"gradient" => BgKind::Gradient,
"image" => BgKind::Image,
_ => return Err(bad()),
})),
"color" => Message::PdfDialog(P::BgColor(0, rgb()?)),
"top" => Message::PdfDialog(P::BgColor(1, rgb()?)),
"middle" => Message::PdfDialog(P::BgColor(2, rgb()?)),
"bottom" => Message::PdfDialog(P::BgColor(3, rgb()?)),
"three" => Message::PdfDialog(P::BgThree(value == "1")),
"rotation" => Message::PdfDialog(P::BgRotation(value.to_string())),
"image" => Message::PdfDialog(P::BgImage(value.to_string())),
"position" => Message::PdfDialog(P::BgPosition(match value {
"center" => BgPosition::Center,
"stretch" => BgPosition::Stretch,
"tile" => BgPosition::Tile,
_ => return Err(bad()),
})),
"aspect" => Message::PdfDialog(P::BgAspect(value == "1")),
_ => return Err(bad()),
}
}
"pdf_dialog_ok" => {
use crate::ui::window::pdf_dialogs::PdfDialogMsg;
Message::PdfDialog(match self.active_modal {
+6
View File
@@ -567,6 +567,8 @@ pub(super) struct OpenCADStudio {
field_dialog: Option<crate::ui::window::field_dialog::FieldDialogState>,
point_cloud_color_map: Option<crate::ui::window::pdf_dialogs::PointCloudColorMapState>,
pc_section: Option<crate::ui::window::pdf_dialogs::PcSectionState>,
/// -VIEW sEttings Background dialog.
view_background: Option<crate::ui::window::pdf_dialogs::ViewBackgroundState>,
underlay_layers: Option<crate::ui::window::pdf_dialogs::UnderlayLayersState>,
pdf_import_settings: Option<crate::modules::insert::pdf_import::PdfImportSettings>,
pdf_import_file: Option<crate::ui::window::pdf_dialogs::PdfImportFileState>,
@@ -1775,6 +1777,8 @@ pub enum ColorPickTarget {
LayerState(usize),
/// The MText editor's selection (or global) colour.
MText,
/// A colour of the view Background dialog: 0 solid, 1 top, 2 middle, 3 bottom.
ViewBackground(u8),
}
/// Table records the clipboard entities depend on, snapshotted from the source
@@ -2115,6 +2119,7 @@ pub enum ModalKind {
PointCloudAttach,
PointCloudColorMap,
PcSection,
ViewBackground,
UnderlayLayers,
PdfImportSettings,
PdfImportFile,
@@ -4494,6 +4499,7 @@ impl OpenCADStudio {
field_dialog: None,
point_cloud_color_map: None,
pc_section: None,
view_background: None,
underlay_layers: None,
pdf_import_settings: None,
pdf_import_file: None,
+7
View File
@@ -9315,6 +9315,13 @@ impl OpenCADStudio {
self.close_sheet_set_dialog();
return Task::none();
}
// The Background dialog returns to -VIEW's settings prompt.
if self.active_modal == Some(super::ModalKind::ViewBackground) {
self.view_background = None;
self.close_active_modal();
let i = self.active_tab;
return self.run_dash_view("SETTINGS", i);
}
if self.active_modal == Some(super::ModalKind::Field) {
self.close_field_dialog();
return Task::none();
+12
View File
@@ -1162,6 +1162,18 @@ impl OpenCADStudio {
crate::ui::window::gradient_editor::GradientMsg::Color(index, color),
)))
}
Some(crate::app::ColorPickTarget::ViewBackground(slot)) => {
let rgb = match color {
codec::types::Color::Rgb { r, g, b } => Some([r, g, b]),
codec::types::Color::Index(index) => {
codec::types::aci_table::aci_to_rgb(index).map(|(r, g, b)| [r, g, b])
}
_ => None,
};
rgb.map(|rgb| {
Message::PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg::BgColor(slot, rgb))
})
}
Some(crate::app::ColorPickTarget::GraphicAttributesLine) => Some(
Message::GraphicAttributes(GraphicAttributesMsg::LineColor(color)),
),
+22 -7
View File
@@ -1090,6 +1090,28 @@ impl OpenCADStudio {
.push_output(crate::tf!("Viewport visual style: {label}").as_ref());
return Task::none();
}
self.apply_render_mode(i, mode, label);
// The viewport record carries the style the mode is saved as.
self.tabs[i]
.scene
.set_active_visual_style_named(crate::modules::view::visual_style::style_name_for(mode));
self.command_line
.push_output(crate::tf!("Visual style: {label}").as_ref());
// The point cloud move gizmo follows the visual style.
if i == self.active_tab {
self.refresh_selected_grips();
}
Task::none()
}
/// The model tab's visual style, without a message.
pub(crate) fn apply_render_mode(
&mut self,
i: usize,
mode: codec::entities::ViewportRenderMode,
label: &str,
) {
use codec::entities::ViewportRenderMode as M;
self.tabs[i].render_mode = mode;
// Write the style onto the active Model tile alone so it
// sticks when that tile loses focus and the other tiles keep
@@ -1105,13 +1127,6 @@ impl OpenCADStudio {
// pipeline keys its upload cache off `geometry_epoch`.
self.tabs[i].scene.bump_geometry_no_blocks();
self.tabs[i].dirty = true;
self.command_line
.push_output(crate::tf!("Visual style: {label}").as_ref());
// The point cloud move gizmo follows the visual style.
if i == self.active_tab {
self.refresh_selected_grips();
}
Task::none()
}
pub(super) fn on_cursor_moved(
+4 -2
View File
@@ -30,6 +30,7 @@ pub(super) fn viewport_controls<'a>(
tile_count: usize,
render_mode_menu_open: bool,
render_mode_preview: Option<codec::entities::ViewportRenderMode>,
style_name: Option<String>,
) -> Element<'a, Message> {
let render_modes: Vec<RenderModeChoice> = crate::modules::view::visual_style::VISUAL_STYLES
.iter()
@@ -98,7 +99,7 @@ pub(super) fn viewport_controls<'a>(
// flyout keeps the current style unchanged while hovering; a click commits.
let picker_head = button(
row![
text(RenderModeChoice(render_mode).to_string()).size(11),
text(style_name.unwrap_or_else(|| RenderModeChoice(render_mode).to_string())).size(11),
crate::ui::icons::themed_arrow_toggle(render_mode_menu_open, 9.0),
]
.spacing(6)
@@ -379,7 +380,7 @@ mod tests {
#[test]
fn test_viewport_controls_construction() {
for style in crate::modules::view::visual_style::VISUAL_STYLES {
let _el = viewport_controls(style.mode, true, true, true, 1, false, None);
let _el = viewport_controls(style.mode, true, true, true, 1, false, None, None);
let _el_open = viewport_controls(
style.mode,
false,
@@ -388,6 +389,7 @@ mod tests {
2,
true,
Some(M::Wireframe3D),
None,
);
}
}
+14
View File
@@ -1297,6 +1297,18 @@ bg={bg_ms:.1}ms n={view_count}"
let rect = tab.scene.active_model_tile_bounds(vw, vh);
// Unified control chip: split buttons + render-mode picker +
// grid / grid-snap toggles, for the active Model tile.
// A named style (Realistic, …) the tile draws as shows by name.
let style_name = match tab.scene.document.objects.get(&tab.scene.active_visual_style()) {
Some(codec::objects::ObjectType::VisualStyle(style)) => {
match crate::modules::view::visual_style::mode_for_style_name(&style.description) {
Some((mode, true)) if mode == tab.render_mode => {
Some(crate::t!(style.description.as_str()).into_owned())
}
_ => None,
}
}
_ => None,
};
let bar = viewport_controls(
tab.render_mode,
self.show_grid,
@@ -1305,6 +1317,7 @@ bg={bg_ms:.1}ms n={view_count}"
tab.scene.model_tiles.borrow().len(),
self.render_mode_menu_open,
self.render_mode_preview,
style_name,
);
// Adaptive: DensitySwap measures the bar's real width every frame
// (reported into `render_bar_w`, which the ViewCube reads to decide
@@ -1397,6 +1410,7 @@ bg={bg_ms:.1}ms n={view_count}"
0,
self.render_mode_menu_open,
self.render_mode_preview,
None,
);
let adaptive: Element<'_, Message> = DensitySwap::new(vec![
iced::widget::opaque(bar),
+11
View File
@@ -35,6 +35,11 @@ impl OpenCADStudio {
Some(K::PointCloudAttach) => crate::t!("Attach Point Cloud").into_owned(),
Some(K::PointCloudColorMap) => crate::t!("Point Cloud Color Map").into_owned(),
Some(K::PcSection) => crate::t!("Extract Section Lines from Point Cloud").into_owned(),
Some(K::ViewBackground) => format!(
"{} — {}",
crate::tr!("view-background", "title"),
self.view_background.as_ref().map_or("", |s| s.view.as_str())
),
Some(K::UnderlayLayers) => crate::t!("Underlay Layers").into_owned(),
Some(K::PdfImportSettings) => crate::t!("PDF Import Settings").into_owned(),
Some(K::PdfImportFile) => crate::t!("Import PDF").into_owned(),
@@ -512,6 +517,12 @@ impl OpenCADStudio {
crate::ui::window::pdf_dialogs::view_pc_section(state, flow)
})
}
super::super::ModalKind::ViewBackground => {
let state = self.view_background.as_ref()?;
sized_flow(ex, 420, 560, |flow| {
crate::ui::window::pdf_dialogs::view_background(state, flow)
})
}
super::super::ModalKind::UnderlayLayers => {
let state = self.underlay_layers.as_ref()?;
sized_flow(ex, 460, 520, |flow| {