refactor(shell): promote inspector_window to openpencil-desktop binary crate
The native runner outgrew the `examples/` slot — it owns DPI tracking, caret-blink animation timer, panel-resize cursor, the full Cmd+wheel / PinchGesture / Pixel/LineDelta dispatch table, etc. None of that is a sample, so it's been promoted to a real crate. * New crate `crates/openpencil-desktop/` with a single `[[bin]]` target. Depends on `openpencil-shell-native` (lib) + winit + skia-safe (gl), gated to macOS / Linux / Windows. * `examples/inspector_window.rs` removed; equivalent code lives at `crates/openpencil-desktop/src/main.rs` with the structs renamed (DesktopApp / paint) and the doc-block rewritten as a runner spec. * Run command: `cargo run -p openpencil-desktop --release`. Old command (`--example inspector_window`) is gone. * Workspace glob `crates/*` already picks up the new crate, no Cargo.toml workspace edit needed. * Docs: crates/CLAUDE.md updated with the new crate row and runner section retitled "Desktop binary". Top-bar layout test renamed + uses the TOP_BAR_HEIGHT constant so future height tweaks stop breaking it.
This commit is contained in:
parent
967201162a
commit
db69fc5fc7
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -1422,6 +1422,16 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|||
name = "openpencil-app"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "openpencil-desktop"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"openpencil-shell-core",
|
||||
"openpencil-shell-native",
|
||||
"skia-safe",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openpencil-shell-core"
|
||||
version = "0.1.0"
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ Native + web editor chrome implemented in Rust against jian-skia. Goal: TS-equiv
|
|||
```
|
||||
crates/
|
||||
├── openpencil-shell-core/ Platform-free widgets + Document model + RenderBackend trait
|
||||
├── openpencil-shell-native/ Desktop runner: winit + skia-safe + accesskit
|
||||
├── openpencil-shell-native/ Native lib: WidgetHostNative + NativeBackend + SharedSkiaContext
|
||||
├── openpencil-shell-web/ Browser runner: wasm32-unknown-unknown + skia-safe-op fork
|
||||
├── openpencil-desktop/ Desktop binary: winit event loop + skia-safe GL surface
|
||||
└── wasm-libc-shim/ ~95 env.* shims (libc / libm / libcxx) for the wasm32 build
|
||||
```
|
||||
|
||||
|
|
@ -82,18 +83,20 @@ resize / dpi_scale
|
|||
|
||||
`stroke_svg_path` parses lucide d-strings via `skia_safe::utils::parse_path::from_svg`. PaintCap::Round + PaintJoin::Round to match lucide's stroke style.
|
||||
|
||||
## Native runner (`shell-native/`)
|
||||
## Desktop binary (`openpencil-desktop/`)
|
||||
|
||||
Entry: `examples/inspector_window.rs` — winit ApplicationHandler, GL surface via `jian-skia`. Logs:
|
||||
`crates/openpencil-desktop/src/main.rs` is the production desktop entry. It owns the winit `ApplicationHandler`, opens a GL window via `SharedSkiaContext::new_desktop`, and dispatches every `WindowEvent` onto `WidgetHostNative::apply_*`. Behaviour:
|
||||
|
||||
- DPI scale via `canvas.scale((dpi, dpi))` per frame (preceded by `reset_matrix()` so it doesn't compound)
|
||||
- LOGICAL viewport sizes (physical / dpi)
|
||||
- Cursor position cached on `CursorMoved`, dispatched on `MouseInput`
|
||||
- `MouseScrollDelta::PixelDelta` → trackpad pan; `LineDelta` / `PinchGesture` → zoom; modifier (Cmd/Ctrl) promotes pixel-delta to zoom
|
||||
- Cursor flips to `EwResize` when over a panel-resize gutter (`host.panel_resize_hover`)
|
||||
- `WaitUntil(host.next_animation_deadline_ms())` pumps the caret-blink redraw
|
||||
|
||||
Native font path bypasses jian-skia's `textlayout` (which builds a fresh `FontCollection` per call → 605ms chrome frame): `NativeBackend` caches a Roboto Typeface + a system CJK Typeface (resolved via `FontMgr::match_family_style_character('一')`) and renders via `Canvas::draw_str`.
|
||||
Native font path bypasses jian-skia's `textlayout` (which builds a fresh `FontCollection` per call → 605ms chrome frame): `NativeBackend` caches a Roboto Typeface + per-codepoint system fonts (resolved via `FontMgr::match_family_style_character`, cached per `i32`) so multi-script chrome (한국어 / हिन्दी / ไทย / Tiếng Việt) renders against the right font. `draw_text` segments each run by typeface and dispatches each segment via `Canvas::draw_str`.
|
||||
|
||||
Run: `cargo run -p openpencil-shell-native --example inspector_window --release`.
|
||||
Run: `cargo run -p openpencil-desktop --release`.
|
||||
|
||||
## Web runner (`shell-web/`)
|
||||
|
||||
|
|
|
|||
30
crates/openpencil-desktop/Cargo.toml
Normal file
30
crates/openpencil-desktop/Cargo.toml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "openpencil-desktop"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
description = "OpenPencil desktop runner — winit + skia-safe binary that drives the Rust shell on macOS / Linux / Windows"
|
||||
|
||||
[[bin]]
|
||||
name = "openpencil-desktop"
|
||||
path = "src/main.rs"
|
||||
|
||||
# Desktop-only binary. The shell-native lib is multi-platform (mobile
|
||||
# stubs land in Step 1f) but the runner here pulls winit + glutin via
|
||||
# shell-native's desktop dep set, so the whole crate is gated to
|
||||
# macos / linux / windows.
|
||||
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
|
||||
openpencil-shell-core = { path = "../openpencil-shell-core", version = "0.1.0" }
|
||||
openpencil-shell-native = { path = "../openpencil-shell-native", version = "0.1.0" }
|
||||
skia-safe = { version = "0.97.0", default-features = false, features = [
|
||||
"binary-cache",
|
||||
"textlayout",
|
||||
"gl",
|
||||
] }
|
||||
winit = { version = "0.30.13", default-features = false, features = [
|
||||
"x11",
|
||||
"wayland",
|
||||
"wayland-csd-adwaita",
|
||||
"rwh_06",
|
||||
] }
|
||||
|
|
@ -1,33 +1,42 @@
|
|||
//! Step 1b §1.4 native inspector demo — paints the four shell-core
|
||||
//! widgets (Tree / PropertyRow / Dropdown / TextInput) through
|
||||
//! `WidgetHostNative` + `NativeFrameBackend`. Visually mirrors the
|
||||
//! shell-web `mount()` first frame so the cross-platform widget
|
||||
//! claim from spec §1.4 is concrete: same widget code, same paint
|
||||
//! output on macOS / Linux / Windows desktop and on
|
||||
//! wasm32-unknown-unknown browsers.
|
||||
//! OpenPencil desktop runner.
|
||||
//!
|
||||
//! winit + skia-safe binary that wires `WidgetHostNative` (from
|
||||
//! `openpencil-shell-native`) into a real OS window. Owns the
|
||||
//! event loop, GL surface, DPI tracking, animation timer, and the
|
||||
//! cursor / input plumbing — everything that's specific to "OS
|
||||
//! window with skia GL surface" and not shared with the wasm32
|
||||
//! browser host or the (Step 1f) mobile shells.
|
||||
//!
|
||||
//! ### Run
|
||||
//!
|
||||
//! ### Run (desktop)
|
||||
//! ```text
|
||||
//! cargo run -p openpencil-shell-native --example inspector_window
|
||||
//! cargo run -p openpencil-desktop --release
|
||||
//! ```
|
||||
//!
|
||||
//! ### Mobile (iOS / Android) — Step 1f
|
||||
//! This example is desktop-only (winit + `SharedSkiaContext::
|
||||
//! new_desktop`). Mobile shells will land their own runners using
|
||||
//! the platform `GlContextProvider` (`EaglProvider` on iOS,
|
||||
//! `AndroidEglProvider` on Android — both zero-sized placeholders
|
||||
//! in shell-native today; spec §11 + 2026-05-10 user directive
|
||||
//! "安卓和ios 不需要 ipc / 本地 cli — 只需要 custom provider").
|
||||
//! The widget glue (`WidgetHostNative` + `NativeFrameBackend`) is
|
||||
//! platform-agnostic; mobile runners reuse the same
|
||||
//! `host.paint(&mut frame, width)` once their provider ships real
|
||||
//! `make_current` / `swap_buffers` impls.
|
||||
//! ### Layout
|
||||
//!
|
||||
//! The window should display a 280 px column on the left containing
|
||||
//! a 3-row tree (Frame / Title / Button — first row selected blue),
|
||||
//! a "Width 960" property row, a "Normal" dropdown, and a "Frame 1"
|
||||
//! text input. CI verifies `cargo build --examples` only — visual
|
||||
//! verification is the Phase E manual smoke responsibility.
|
||||
//! - `WidgetHostNative` (in `openpencil-shell-native`) owns the
|
||||
//! `Document`, paints widgets, and routes input — platform-free
|
||||
//! beyond the `RenderBackend` impl.
|
||||
//! - `SharedSkiaContext` + `NativeBackend` (also in shell-native)
|
||||
//! wrap jian-skia + glutin so the same widget code paints on
|
||||
//! macOS / Linux / Windows.
|
||||
//! - This binary glues winit's `ApplicationHandler` events
|
||||
//! (Resumed / Resized / RedrawRequested / CursorMoved /
|
||||
//! MouseInput / MouseWheel / PinchGesture / KeyboardInput /
|
||||
//! ModifiersChanged / ScaleFactorChanged) onto the host's
|
||||
//! `apply_*` methods + `paint`.
|
||||
//!
|
||||
//! ### Mobile (iOS / Android) — Step 1f
|
||||
//!
|
||||
//! The desktop crate is gated to macOS / Linux / Windows. Mobile
|
||||
//! shells will live in their own crates with platform-specific
|
||||
//! `GlContextProvider` impls (`EaglProvider` on iOS,
|
||||
//! `AndroidEglProvider` on Android — both already stubbed in
|
||||
//! shell-native; spec §11 + 2026-05-10 directive). The widget glue
|
||||
//! `WidgetHostNative` is platform-agnostic; mobile runners reuse
|
||||
//! `host.paint(&mut frame, w, h)` once their provider ships real
|
||||
//! `make_current` / `swap_buffers`.
|
||||
|
||||
#![cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
|
||||
|
|
@ -40,24 +49,22 @@ use winit::application::ApplicationHandler;
|
|||
use winit::event::{
|
||||
ElementState, KeyEvent, MouseButton, MouseScrollDelta, StartCause, WindowEvent,
|
||||
};
|
||||
use winit::event_loop::ControlFlow;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
/// Default starting viewport — matches `with_inner_size` below so
|
||||
/// the first frame has the right layout. Resize is handled in the
|
||||
/// `Resized` arm: we re-cache the host viewport via `set_viewport`
|
||||
/// so the next paint matches the new physical size.
|
||||
/// `Resized` arm: we cache the LOGICAL viewport so the next paint
|
||||
/// matches the new size at DPI-independent coordinates.
|
||||
const INITIAL_VIEWPORT_W: f32 = 1440.0;
|
||||
const INITIAL_VIEWPORT_H: f32 = 900.0;
|
||||
|
||||
/// Paint pass — clear to white, then dispatch the editor-UI
|
||||
/// composition through `WidgetHostNative`. Pulled into a free
|
||||
/// function so the initial `Resumed` paint and `RedrawRequested`
|
||||
/// redraws share the exact same draw list (same pattern as
|
||||
/// `basic_window`).
|
||||
fn paint_inspector(
|
||||
/// Paint pass — clear, scale by DPI, dispatch to the widget host.
|
||||
/// The matrix `reset_matrix` is required because skia's canvas
|
||||
/// matrix is stateful across `with_frame` invocations; without it
|
||||
/// `scale(dpi, dpi)` would compound per redraw.
|
||||
fn paint(
|
||||
ctx: &mut SharedSkiaContext,
|
||||
backend: &mut NativeBackend,
|
||||
host: &WidgetHostNative,
|
||||
|
|
@ -68,12 +75,6 @@ fn paint_inspector(
|
|||
ctx.begin_frame();
|
||||
ctx.with_frame(|canvas, _glow| {
|
||||
canvas.clear(skia_safe::Color::BLACK);
|
||||
// Skia canvas matrix is stateful across `with_frame`
|
||||
// invocations — without an explicit reset, every redraw
|
||||
// applies `scale(dpi, dpi)` on top of the previous frame's
|
||||
// matrix, doubling content size per click. Reset to
|
||||
// identity so the per-frame scale is absolute, not
|
||||
// cumulative. (Step 4 stop-hook fix: "click → 突然放大".)
|
||||
canvas.reset_matrix();
|
||||
canvas.scale((dpi, dpi));
|
||||
let mut frame = NativeFrameBackend::new(backend, canvas);
|
||||
|
|
@ -89,40 +90,38 @@ fn paint_inspector(
|
|||
ctx.present();
|
||||
}
|
||||
|
||||
struct InspectorApp {
|
||||
struct DesktopApp {
|
||||
window: Option<Window>,
|
||||
ctx: Option<SharedSkiaContext>,
|
||||
backend: Option<NativeBackend>,
|
||||
host: WidgetHostNative,
|
||||
/// Cached LOGICAL viewport size — refreshed on Resumed +
|
||||
/// Resized so the host paints into the right rect at logical
|
||||
/// (DPI-independent) coordinates. The canvas is scaled by DPI
|
||||
/// inside `paint_inspector`, so widget paint coords are
|
||||
/// logical px and the surface (set up by `SharedSkiaContext`)
|
||||
/// is physical.
|
||||
/// Resized so the host paints at DPI-independent coordinates.
|
||||
/// The canvas is scaled by DPI inside `paint`; the underlying
|
||||
/// skia surface (set up by `SharedSkiaContext`) is physical.
|
||||
viewport_width: f32,
|
||||
viewport_height: f32,
|
||||
/// Last cursor position (logical, top-left origin). winit
|
||||
/// Last cursor position (logical, top-left origin). winit's
|
||||
/// `CursorMoved` reports a `PhysicalPosition`; we divide by
|
||||
/// the cached DPI so `WidgetHostNative::apply_click` matches
|
||||
/// the same logical coordinate space the widget paints used.
|
||||
/// the cached DPI so `WidgetHostNative::apply_*` receives
|
||||
/// the same logical coordinate space the widgets paint in.
|
||||
cursor_x: f32,
|
||||
cursor_y: f32,
|
||||
/// Cached scale factor — refreshed on Resumed +
|
||||
/// `ScaleFactorChanged`. Used to scale the canvas + convert
|
||||
/// physical cursor positions to logical.
|
||||
/// `ScaleFactorChanged`. Drives the DPI scale on the canvas
|
||||
/// and the physical→logical cursor conversion.
|
||||
dpi: f32,
|
||||
/// Cmd / Ctrl held — promotes 2-finger swipe from pan to zoom.
|
||||
zoom_modifier: bool,
|
||||
/// Monotonic clock anchor — `Instant.elapsed().as_millis()`
|
||||
/// from this is fed into `WidgetHostNative::set_now_ms` so
|
||||
/// `jian_core::anim::blink_visible` can drive the caret
|
||||
/// blink (and any future time-based UI animation).
|
||||
/// `jian_core::anim::blink_visible` can drive the caret blink
|
||||
/// (and any future time-based UI animation).
|
||||
clock_start: Instant,
|
||||
error: Option<SharedSkiaError>,
|
||||
}
|
||||
|
||||
impl InspectorApp {
|
||||
impl DesktopApp {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
window: None,
|
||||
|
|
@ -141,7 +140,7 @@ impl InspectorApp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for InspectorApp {
|
||||
impl ApplicationHandler for DesktopApp {
|
||||
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
// When the WaitUntil deadline fires, the next redraw paints
|
||||
// the next caret-blink phase. winit doesn't auto-redraw on
|
||||
|
|
@ -158,7 +157,7 @@ impl ApplicationHandler for InspectorApp {
|
|||
return;
|
||||
}
|
||||
let attrs = Window::default_attributes()
|
||||
.with_title("OpenPencil — inspector_window (Step 3 native)")
|
||||
.with_title("OpenPencil")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(
|
||||
INITIAL_VIEWPORT_W as u32,
|
||||
INITIAL_VIEWPORT_H as u32,
|
||||
|
|
@ -166,7 +165,7 @@ impl ApplicationHandler for InspectorApp {
|
|||
let window = match event_loop.create_window(attrs) {
|
||||
Ok(w) => w,
|
||||
Err(err) => {
|
||||
eprintln!("inspector_window: create_window failed: {err}");
|
||||
eprintln!("openpencil-desktop: create_window failed: {err}");
|
||||
event_loop.exit();
|
||||
return;
|
||||
}
|
||||
|
|
@ -180,7 +179,7 @@ impl ApplicationHandler for InspectorApp {
|
|||
self.backend = Some(NativeBackend::with_dpi(dpi));
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("inspector_window: SharedSkiaContext::new_desktop failed: {err}");
|
||||
eprintln!("openpencil-desktop: SharedSkiaContext::new_desktop failed: {err}");
|
||||
self.error = Some(err);
|
||||
event_loop.exit();
|
||||
return;
|
||||
|
|
@ -189,7 +188,7 @@ impl ApplicationHandler for InspectorApp {
|
|||
self.window = Some(window);
|
||||
|
||||
if let (Some(ctx), Some(backend)) = (self.ctx.as_mut(), self.backend.as_mut()) {
|
||||
paint_inspector(
|
||||
paint(
|
||||
ctx,
|
||||
backend,
|
||||
&self.host,
|
||||
|
|
@ -210,8 +209,7 @@ impl ApplicationHandler for InspectorApp {
|
|||
// WindowEvent so `apply_press` / `apply_text` /
|
||||
// `apply_backspace` etc. stamp `caret_anchor_ms` with the
|
||||
// CURRENT timestamp, not the one captured at the previous
|
||||
// RedrawRequested (codex Step 6 stop-hook fix: "caret
|
||||
// reset can use a stale clock").
|
||||
// RedrawRequested.
|
||||
let now_ms = self.clock_start.elapsed().as_millis() as u64;
|
||||
self.host.set_now_ms(now_ms);
|
||||
match event {
|
||||
|
|
@ -221,13 +219,11 @@ impl ApplicationHandler for InspectorApp {
|
|||
WindowEvent::Resized(size) => {
|
||||
if let Some(ctx) = self.ctx.as_mut() {
|
||||
if let Err(err) = ctx.resize(size.width, size.height) {
|
||||
eprintln!("inspector_window: resize failed: {err}");
|
||||
eprintln!("openpencil-desktop: resize failed: {err}");
|
||||
self.error = Some(err);
|
||||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
// Cache LOGICAL size — divide physical by DPI so
|
||||
// widget paint coords stay DPI-independent.
|
||||
self.viewport_width = size.width as f32 / self.dpi;
|
||||
self.viewport_height = size.height as f32 / self.dpi;
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
|
|
@ -241,11 +237,8 @@ impl ApplicationHandler for InspectorApp {
|
|||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
// now_ms was already pushed into the host at the
|
||||
// top of `window_event` — paint reads the same
|
||||
// value via `host.now_ms`.
|
||||
if let (Some(ctx), Some(backend)) = (self.ctx.as_mut(), self.backend.as_mut()) {
|
||||
paint_inspector(
|
||||
paint(
|
||||
ctx,
|
||||
backend,
|
||||
&self.host,
|
||||
|
|
@ -254,9 +247,6 @@ impl ApplicationHandler for InspectorApp {
|
|||
self.dpi,
|
||||
);
|
||||
}
|
||||
// Schedule next wake-up at the caret blink boundary
|
||||
// (or back to event-driven `Wait` when no animation
|
||||
// is pending).
|
||||
if let Some(deadline_ms) = self.host.next_animation_deadline_ms() {
|
||||
let deadline = self.clock_start + Duration::from_millis(deadline_ms);
|
||||
event_loop.set_control_flow(ControlFlow::WaitUntil(deadline));
|
||||
|
|
@ -265,9 +255,6 @@ impl ApplicationHandler for InspectorApp {
|
|||
}
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
// winit `position` is PHYSICAL — convert to logical
|
||||
// so widget hit-tests + drag deltas match the
|
||||
// coordinate space widgets paint in.
|
||||
self.cursor_x = position.x as f32 / self.dpi;
|
||||
self.cursor_y = position.y as f32 / self.dpi;
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
|
|
@ -322,11 +309,11 @@ impl ApplicationHandler for InspectorApp {
|
|||
}
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
// Figma-style routing: PixelDelta (trackpad
|
||||
// 2-finger swipe) pans, LineDelta (mouse wheel)
|
||||
// zooms. Cmd / Ctrl held promotes a pixel-delta
|
||||
// gesture to zoom too, since trackpad-only laptops
|
||||
// need a way to zoom without a pinch sensor.
|
||||
// Figma-style routing: PixelDelta (trackpad 2-finger
|
||||
// swipe) pans, LineDelta (mouse wheel) zooms. Cmd /
|
||||
// Ctrl held promotes pixel-delta to zoom too, since
|
||||
// trackpad-only laptops need a way to zoom without
|
||||
// a pinch sensor.
|
||||
let consumed = match delta {
|
||||
MouseScrollDelta::LineDelta(_, y) => self.host.apply_wheel(
|
||||
self.cursor_x,
|
||||
|
|
@ -365,9 +352,6 @@ impl ApplicationHandler for InspectorApp {
|
|||
}
|
||||
}
|
||||
WindowEvent::PinchGesture { delta, .. } => {
|
||||
// macOS pinch — emit as zoom centered on cursor.
|
||||
// `delta` is a unitless ratio per pinch tick; scale
|
||||
// it up so it feels comparable to wheel zoom.
|
||||
let consumed = self.host.apply_wheel(
|
||||
self.cursor_x,
|
||||
self.cursor_y,
|
||||
|
|
@ -382,8 +366,6 @@ impl ApplicationHandler for InspectorApp {
|
|||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(mods) => {
|
||||
// Track Cmd / Ctrl so 2-finger swipe can be
|
||||
// promoted to zoom while held.
|
||||
let state = mods.state();
|
||||
self.zoom_modifier = state.super_key() || state.control_key();
|
||||
}
|
||||
|
|
@ -428,7 +410,7 @@ impl ApplicationHandler for InspectorApp {
|
|||
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
if let Some(mut ctx) = self.ctx.take() {
|
||||
if let Err(err) = ctx.teardown() {
|
||||
eprintln!("inspector_window: teardown failed: {err}");
|
||||
eprintln!("openpencil-desktop: teardown failed: {err}");
|
||||
}
|
||||
}
|
||||
self.backend.take();
|
||||
|
|
@ -440,18 +422,18 @@ fn main() {
|
|||
let event_loop = match EventLoop::new() {
|
||||
Ok(el) => el,
|
||||
Err(err) => {
|
||||
eprintln!("inspector_window: EventLoop::new failed: {err}");
|
||||
eprintln!("openpencil-desktop: EventLoop::new failed: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
|
||||
let mut app = InspectorApp::new();
|
||||
event_loop.set_control_flow(ControlFlow::Wait);
|
||||
let mut app = DesktopApp::new();
|
||||
if let Err(err) = event_loop.run_app(&mut app) {
|
||||
eprintln!("inspector_window: run_app exited with error: {err}");
|
||||
eprintln!("openpencil-desktop: run_app exited with error: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if let Some(err) = app.error {
|
||||
eprintln!("inspector_window: fatal error during run: {err}");
|
||||
eprintln!("openpencil-desktop: fatal error during run: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -329,7 +329,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn layout_reports_full_width_and_48_height() {
|
||||
fn layout_reports_full_width_and_top_bar_height() {
|
||||
let bar = TopBar::untitled();
|
||||
let cx = LayoutCx {
|
||||
available_width: 1000.0,
|
||||
|
|
@ -337,7 +337,7 @@ mod tests {
|
|||
};
|
||||
let lb = bar.layout(&cx);
|
||||
assert_eq!(lb.rect.size.x, 1000.0);
|
||||
assert_eq!(lb.rect.size.y, 48.0);
|
||||
assert_eq!(lb.rect.size.y, TOP_BAR_HEIGHT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue