mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 19:27:07 -04:00
W: New Window in its own OS window
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
79f266c04c
commit
1c8952f6db
4 files changed
+93
-10
No files matched your search
+2
-2
@@ -93,7 +93,7 @@ DesignCraft aims at full Adobe InDesign parity — and to be better: faster, ope
|
||||
- Graphic cells (`table.placeGraphic`, `table.textCell`, Table › Convert Cell Type, Table panel): images in table cells, fitted or filled, clipped to the cell on screen and in PDF.
|
||||
- Live Distribute (Space while dragging a selection handle; `transform.resize {distribute}`): objects keep their size and spread with the bounds.
|
||||
- Liquid Layout (`liquid.pageRule/object`, `guide.liquid`, `layout.createAlternate`, Liquid Layout panel): scale / re-center / guide-based / object-based page rules applied on page resize and Document Setup; dashed liquid guides; alternate layouts as resized page copies in a named section. Alternate layouts aren't linked to their source.
|
||||
- Split Window / New Window (`window.split`, `window.newWindow`, Window ▸ Arrange): two side-by-side views of the document with their own zoom, scroll and render cache. New Window opens that second view rather than a separate OS window.
|
||||
- Split Window / New Window (`window.split`, `window.newWindow`, Window ▸ Arrange): two side-by-side views of the document with their own zoom, scroll and render cache. New Window puts the second view in its own OS window (an egui viewport; a floating window on the web).
|
||||
- Transparency flattener (`file.exportPdf {flatten: high|medium|low|ppi}`, Edit ▸ Transparency Flattener Presets): each object involving transparency becomes an opaque image of its area over what's beneath it, so everything else stays vector (spreads with transparent parent items are rasterised whole).
|
||||
- Video and sound (place .mp4/.mov/.webm/.mp3/.wav…, `media.options/get`, Media panel): media frames show a poster or placeholder on screen, in print and in PDF, and export as HTML5 `<video>`/`<audio>` in EPUB. No in-app playback or PDF rich media.
|
||||
- Right-to-left text (World-Ready basics): right-to-left runs are shaped right to left, lines are reordered by the Unicode Bidirectional Algorithm (matched against the reference implementation), Paragraph Direction (`type.para {direction}`, Paragraph panel, IDML ParagraphDirection, EPUB `dir`). Kashida justification, caret movement in visual order and digit substitution aren't there yet.
|
||||
@@ -177,7 +177,7 @@ four agents in parallel on separate crates:
|
||||
| Work | Estimate |
|
||||
|---|---|
|
||||
| Open P0 (2: PDF/X-4 validation against a certified checker, PostScript interpretation for EPS) | 8 h |
|
||||
| Open P1 (3 partial: New Window as a separate OS window, language support, on-screen blending in the blend space) | 10 h |
|
||||
| Open P1 (1 partial: on-screen blending in the blend space) | 4 h |
|
||||
| Open P2 (16 partial: vertical/RTL refinements, alternate-layout linking, separations from source colours, buttons as form fields, interactive PDF extras, scripting, localization breadth, …) | 45 h |
|
||||
| Depth and pixel fidelity of every dialog, panel and menu against InDesign 2026 | 90 h |
|
||||
| Performance (incremental composition, GPU raster) and interchange hardening (IDML/PDF corpus) | 55 h |
|
||||
|
||||
@@ -332,6 +332,8 @@ pub struct DesignApp {
|
||||
pub canvas_rect: Option<egui::Rect>,
|
||||
/// Window › Arrange › Split Window: two views of the document side by side.
|
||||
pub split: bool,
|
||||
/// Window › Arrange › New Window: the second view in its own window (pane 1).
|
||||
pub second_window: bool,
|
||||
/// The current pane (0 or 1): views, cache and rect refer to it.
|
||||
pub pane: u8,
|
||||
/// The pane last clicked (menu zoom and scroll go there).
|
||||
@@ -372,6 +374,7 @@ impl DesignApp {
|
||||
canvas: CanvasCache::new(),
|
||||
canvas_rect: None,
|
||||
split: false,
|
||||
second_window: false,
|
||||
pane: 0,
|
||||
focus_pane: 0,
|
||||
other_pane: None,
|
||||
@@ -666,10 +669,48 @@ impl DesignApp {
|
||||
self.switch_pane(self.focus_pane);
|
||||
} else {
|
||||
self.switch_pane(0);
|
||||
self.focus_pane = 0;
|
||||
// With a second window, the main one takes the focus back when clicked.
|
||||
if !self.second_window || (ui.input(|i| i.pointer.any_pressed()) && ui.ui_contains_pointer()) {
|
||||
self.focus_pane = 0;
|
||||
}
|
||||
canvas::show(self, ui);
|
||||
}
|
||||
});
|
||||
// New Window: pane 1 in a window of its own.
|
||||
if self.second_window
|
||||
&& !presenting
|
||||
&& let Some(title) = self.session.active().map(|d| format!("{} — 2", d.doc.title))
|
||||
{
|
||||
let fill = t.pasteboard;
|
||||
let ctx = ui.ctx().clone();
|
||||
ctx.show_viewport_immediate(
|
||||
egui::ViewportId::from_hash_of("designcraft_second_window"),
|
||||
egui::ViewportBuilder::default().with_title(title).with_inner_size([960.0, 720.0]),
|
||||
|ui, _class| {
|
||||
egui::CentralPanel::default().frame(egui::Frame::NONE.fill(fill)).show(ui, |ui| {
|
||||
self.switch_pane(1);
|
||||
if self.view().is_none() {
|
||||
self.switch_pane(0);
|
||||
let v = self.view().copied();
|
||||
self.switch_pane(1);
|
||||
if let (Some(mut v), Some(m)) = (v, self.view_mut()) {
|
||||
v.fitted = false;
|
||||
*m = v;
|
||||
}
|
||||
}
|
||||
if ui.input(|i| i.pointer.any_pressed()) {
|
||||
self.focus_pane = 1;
|
||||
}
|
||||
canvas::show(self, ui);
|
||||
});
|
||||
if ui.input(|i| i.viewport().close_requested()) {
|
||||
self.second_window = false;
|
||||
self.focus_pane = 0;
|
||||
}
|
||||
},
|
||||
);
|
||||
self.switch_pane(self.focus_pane);
|
||||
}
|
||||
dock::flyout(self, &ctx);
|
||||
dock::floating(self, &ctx);
|
||||
story_editor::show(self, &ctx);
|
||||
|
||||
@@ -112,7 +112,7 @@ pub const UI_COMMANDS: &[(&str, &str, Option<&str>, &str)] = &[
|
||||
("window.dockPanel", "Dock Panel", None, "{panel} — back into the dock's icon column"),
|
||||
("window.controlBar", "Control", Some("Cmd+Alt+6"), "{}"),
|
||||
("window.split", "Split Window", None, "{on?: bool} — two views of the document side by side, each with its own zoom and scroll"),
|
||||
("window.newWindow", "New Window", None, "{} — another view of the active document (shown beside the first)"),
|
||||
("window.newWindow", "New Window", None, "{on?: bool} — another view of the active document in its own window"),
|
||||
("window.taskBar", "Contextual Task Bar", None, "{}"),
|
||||
("help.discord", "Join the ArtCraft Discord…", None, "{} — opens https://discord.gg/artcraft in the browser"),
|
||||
("help.appPage", "DesignCraft Website…", None, "{} — opens https://getartcraft.com/apps/designcraft"),
|
||||
@@ -1202,15 +1202,27 @@ pub fn run_ui(app: &mut DesignApp, id: &str, p: &Value) -> Option<Result<Value,
|
||||
flag(&mut app.ui.tag_markers)
|
||||
}
|
||||
"edit.dynamicSpelling" => flag(&mut app.ui.dynamic_spelling),
|
||||
"window.split" | "window.newWindow" => {
|
||||
let on = if id == "window.newWindow" { true } else { p.get("on").and_then(Value::as_bool).unwrap_or(!app.split) };
|
||||
"window.split" => {
|
||||
let on = p.get("on").and_then(Value::as_bool).unwrap_or(!app.split);
|
||||
app.split = on;
|
||||
if !on {
|
||||
if on {
|
||||
app.second_window = false;
|
||||
} else {
|
||||
app.switch_pane(0);
|
||||
app.focus_pane = 0;
|
||||
}
|
||||
Ok(json!(on))
|
||||
}
|
||||
"window.newWindow" => {
|
||||
// The second view moves into its own window (on the web: a floating window).
|
||||
app.second_window = p.get("on").and_then(Value::as_bool).unwrap_or(true);
|
||||
app.split = false;
|
||||
if !app.second_window {
|
||||
app.switch_pane(0);
|
||||
app.focus_pane = 0;
|
||||
}
|
||||
Ok(json!(app.second_window))
|
||||
}
|
||||
"window.taskBar" => flag(&mut app.ui.task_bar),
|
||||
"help.about" => {
|
||||
app.ui.about = p.get("open").and_then(Value::as_bool).unwrap_or(true);
|
||||
@@ -1593,6 +1605,7 @@ pub fn checked(app: &DesignApp, id: &str, params: &Value) -> Option<bool> {
|
||||
"changes.track" => app.session.active().is_some_and(|d| d.doc.settings.track_changes),
|
||||
"window.controlBar" => app.ui.control_bar,
|
||||
"window.split" => app.split,
|
||||
"window.newWindow" => app.second_window,
|
||||
"view.proofColors" => app.ui.proof_colors,
|
||||
"view.flattenerPreview" => app.ui.flattener_preview,
|
||||
"view.tagMarkers" => app.ui.tag_markers,
|
||||
@@ -2116,6 +2129,35 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_window_shows_a_second_view() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
app.session.execute("file.new", &json!({})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
let frame = |app: &mut crate::DesignApp| {
|
||||
let input =
|
||||
egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1400.0, 900.0))), ..Default::default() };
|
||||
let mut out = ctx.run_ui(input, |ui| {
|
||||
app.logic(&ui.ctx().clone());
|
||||
app.ui(ui);
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
};
|
||||
frame(&mut app);
|
||||
assert_eq!(run_ui(&mut app, "window.newWindow", &json!({})).unwrap().unwrap(), json!(true));
|
||||
assert!(!app.split);
|
||||
frame(&mut app);
|
||||
frame(&mut app);
|
||||
// Pane 1 was drawn (its rect is held for it) and has its own view.
|
||||
assert!(app.other_pane.as_ref().and_then(|o| o.1).is_some() || app.pane == 1);
|
||||
app.switch_pane(1);
|
||||
assert!(app.view().is_some(), "the second window has a view");
|
||||
app.switch_pane(0);
|
||||
assert_eq!(checked(&app, "window.newWindow", &Value::Null), Some(true));
|
||||
run_ui(&mut app, "window.newWindow", &json!({"on": false})).unwrap().unwrap();
|
||||
frame(&mut app);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panels_float_and_dock() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
|
||||
+3
-3
@@ -10,12 +10,12 @@ Each row of the InDesign feature catalogue is scored **D** (done), **P** (partia
|
||||
| Priority | Features | Done | Partial | Missing | Parity |
|
||||
|---|---|---|---|---|---|
|
||||
| P0 | 102 | 100 | 2 | 0 | 99% |
|
||||
| P1 | 97 | 95 | 2 | 0 | 99% |
|
||||
| P1 | 97 | 96 | 1 | 0 | 99% |
|
||||
| P2 | 76 | 65 | 11 | 0 | 93% |
|
||||
|
||||
| Area | Parity |
|
||||
|---|---|
|
||||
| Application shell & workspace | 96% |
|
||||
| Application shell & workspace | 99% |
|
||||
| Documents, pages, spreads | 98% |
|
||||
| Layers | 100% |
|
||||
| Frames, shapes & paths | 100% |
|
||||
@@ -43,7 +43,7 @@ Open P0 items: Vector/page formats (PDF/AI/SVG/EPS place), Export to PDF (print)
|
||||
|---|---|---|---|
|
||||
| Application shell & workspace | Start/Home screen | P0 | D |
|
||||
| Application shell & workspace | Multiple documents in tabs | P0 | D |
|
||||
| Application shell & workspace | New Window for same document | P1 | P |
|
||||
| Application shell & workspace | New Window for same document | P1 | D |
|
||||
| Application shell & workspace | Dockable panels | P0 | D |
|
||||
| Application shell & workspace | Workspaces | P1 | D |
|
||||
| Application shell & workspace | Control panel | P0 | D |
|
||||
|
||||
Reference in new issue
Block a user