From 76200795914120d4a7162ec2f4a55e2b7f0e21ae Mon Sep 17 00:00:00 2001 From: uitt_hu Date: Sun, 15 Nov 2026 18:01:39 +0100 Subject: [PATCH 1/2] The card of a new version opens in the middle of the view (#91) The card of Ctrl+S in a project belongs to the window, not to a place in the scene: it stands in the middle of the 3D view like a dialog, without a line to a point of the scene, and stays there while the camera moves (until the mouse moves it). A trait `_centered` of the item of a card chooses this. Co-Authored-By: Claude Opus 5.5 --- src/LiveCard.jl | 13 +++++++++++++ src/LiveCards.jl | 11 ++++++++--- src/LiveProject.jl | 2 ++ test/TestLiveProject.jl | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/LiveCard.jl b/src/LiveCard.jl index 7d288fc..1b6abb3 100644 --- a/src/LiveCard.jl +++ b/src/LiveCard.jl @@ -1014,6 +1014,19 @@ function _spot_position(spot::Tuple{Bool, Bool, Vec2f}, size::Vec2f, view::Rect2 return Point2f(clamp(x, lo[1], max(lo[1], hi[1] - size[1])), clamp(y, min(hi[2], lo[2] + size[2]), hi[2])) end +"""Top left corner [figure px] of a card of the `size` in the middle of the 3D view `view`.""" +_center_position(size::Vec2f, view::Rect2f) = + Point2f(minimum(view)[1] + (Makie.widths(view)[1] - size[1]) / 2, minimum(view)[2] + (Makie.widths(view)[2] + size[2]) / 2) + +""" + _centered(obj) -> Bool + +Whether the card of `obj` belongs to the window rather than to a place in the scene: it stands in +the middle of the 3D view without a line, like a dialog (unless the mouse moved it), e.g. the card +of a new version of a project, see `_VersionCard`. `false` for everything else. +""" +_centered(@nospecialize(obj)) = false + # Rectangle [figure px] of a part of a card, which hangs down from the point of its suggested bounding box function _part_rect(x::GridLayout) p, s = minimum(x.layoutobservables.suggestedbbox[]), _card_size(x) diff --git a/src/LiveCards.jl b/src/LiveCards.jl index d8ebc3d..8e11fce 100644 --- a/src/LiveCards.jl +++ b/src/LiveCards.jl @@ -506,9 +506,13 @@ function _update_card!(gui::LiveView, c::_ComponentCard, @nospecialize(obj), obs _fit_properties!(c) _fit_view!(c, view) size = _card_size(c) - p = isnothing(c.spot) ? _avoid(_card_position(sel, size, view), size, view, obstacles) : + # A card of the window rather than of a place in the scene stands in the middle of the view, + # without a line, unless the mouse moved it, see `_centered` + centered = isnothing(c.spot) && _centered(obj) + p = centered ? _center_position(size, view) : + isnothing(c.spot) ? _avoid(_card_position(sel, size, view), size, view, obstacles) : _spot_position(c.spot, size, view) - if isnothing(c.spot) && c.pinned && !c.collapsed && _covers(p, size, obstacles) + if !centered && isnothing(c.spot) && c.pinned && !c.collapsed && _covers(p, size, obstacles) c.auto_collapsed = true size = _card_size(c) p = _avoid(_card_position(sel, size, view), size, view, obstacles) @@ -516,7 +520,8 @@ function _update_card!(gui::LiveView, c::_ComponentCard, @nospecialize(obj), obs _arrange_card!(c, p) rect = _card_rect(p, size) push!(obstacles, rect) - _update!(c.link, [_link_anchor(scene, corners, obj), Point2f(minimum(rect) .+ size ./ 2)]) + _update!(c.link, _centered(obj) ? Point2f[] : + [_link_anchor(scene, corners, obj), Point2f(minimum(rect) .+ size ./ 2)]) _update!(c.scene.visible, true) _show_view!(gui, c, obj) return nothing diff --git a/src/LiveProject.jl b/src/LiveProject.jl index 36126d0..66876a3 100644 --- a/src/LiveProject.jl +++ b/src/LiveProject.jl @@ -1207,6 +1207,8 @@ struct _VersionCard <: _InfoItem end _points(x::_VersionCard) = [x.point] +# A dialog of the window, not of a place in the scene, see `_centered` +_centered(::_VersionCard) = true _title(::_VersionCard) = "Save a version" _tree_kind(::_VersionCard) = :save _keep_plots!(::LiveView, ::_VersionCard) = nothing diff --git a/test/TestLiveProject.jl b/test/TestLiveProject.jl index 1d14bd3..418ea5d 100644 --- a/test/TestLiveProject.jl +++ b/test/TestLiveProject.jl @@ -382,6 +382,9 @@ const GUI = BeamletOpticsGUI GUI._save_key!(gui) c = GUI._version_card(gui) @test !isnothing(c) + # in the middle of the view like a dialog, without a line to a point of the scene + @test GUI._centered(c.obj) && isempty(c.link[]) + @test GUI._center_position(Vec2f(100, 50), Rect2f(10, 20, 400, 300)) == Point2f(160, 195) box = GUI._version_box(gui) @test box.displayed_string[] == "moved $lens" == version_summary(dir) @test box.focused[] && gui.controls.ignore_keys() From bab700ef545ef028f6cc97bea9c6f5f891f5bc4f Mon Sep 17 00:00:00 2001 From: uitt_hu Date: Sun, 15 Nov 2026 18:05:50 +0100 Subject: [PATCH 2/2] Docs of projects: the card of a new version stands in the middle of the view (#91) Co-Authored-By: Claude Opus 5.5 --- docs/src/projects.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/projects.md b/docs/src/projects.md index 69b0bb0..455c124 100644 --- a/docs/src/projects.md +++ b/docs/src/projects.md @@ -50,8 +50,8 @@ from the catalog are saved with their constructors and can be edited on their pa together, a group moved as a whole as one), new objects appear, changed ones are built again and removed ones go. The window is never replaced. - **Versions.** `Ctrl+S` writes what is still pending and saves a version of the project (a git - commit, without a git install). A card asks for its message, filled with what changed since the - last version. + commit, without a git install). A card in the middle of the view asks for its message, filled + with what changed since the last version. - **Taking the files.** `Ctrl+R` makes the window as in the files: everything that differs is taken from them, changes in the window that were not saved are lost. - **Groups** made in the window are `@group` lines of the project.