forked from 1b-agent/opencadstudio-upstream
-VIEW sEttings Background dialog and view backgrounds
Color, Gradient and Image at the background prompt open a Background dialog (None / Solid / Gradient / Image, preview, solid colour, two- or three-colour gradient with rotation, image file with position and scale). Apply links the view to a new background object in ACAD_BACKGROUND; restoring a view gives the model viewport a copy of it (or none), and saving a view copies the viewport's background. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
cd07141cde
commit
c8167c54ee
34 files changed
+1400
-4
No files matched your search
@@ -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(_)
|
||||
|
||||
@@ -111,7 +111,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 {
|
||||
@@ -197,6 +197,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();
|
||||
@@ -245,14 +253,20 @@ impl OpenCADStudio {
|
||||
/// 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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
view.handle = document.allocate_handle();
|
||||
@@ -339,3 +353,193 @@ impl OpenCADStudio {
|
||||
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,
|
||||
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(¤t) {
|
||||
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;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
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(0.0, 0.0),
|
||||
scale: codec::types::Vector2::new(1.0, 1.0),
|
||||
}))
|
||||
}
|
||||
};
|
||||
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) {
|
||||
view.background_handle = handle;
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
return self.run_dash_view("SETTINGS", i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user