Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 47 additions & 33 deletions +labkit/+ui/createWorkbench.m → +labkit/+ui/+app/createShell.m
Original file line number Diff line number Diff line change
@@ -1,38 +1,52 @@
function ui = createWorkbench(figName, figPosition, leftWidth, opts)
%CREATEWORKBENCH Deprecated compatibility scientific-app shell.
function ui = createShell(spec)
%CREATESHELL Create the standard LabKit app shell from a named spec.
%
% Usage:
% ui = labkit.ui.createWorkbench(titleText, position, leftWidth);
% ui = labkit.ui.createWorkbench(titleText, position, leftWidth, opts);
%
% New app code should call labkit.ui.createAppShell with a spec struct.
% This compatibility entry point is retained for one migration cycle.
% ui = labkit.ui.app.createShell(struct( ...
% 'title', "Example", ...
% 'position', [90 70 1200 800], ...
% 'leftWidth', 360, ...
% 'options', struct('rightKind', 'dualPlot')));
%
% Inputs:
% figName - figure title.
% figPosition - MATLAB figure position [x y width height].
% leftWidth - initial left controls width in pixels.
% opts - optional struct.
%
% Options:
% rightKind - "custom" (default) or "dualPlot".
% rightGridSize - [rows columns], default [1 1] for custom right side.
% rightRowHeight - cell row of grid row heights, default {'1x'}.
% rightRowSpacing - scalar pixels, default 8 or 10 for dualPlot.
% showPlotControls - logical, dualPlot only, default true.
% controlsTitle - left panel title, default "Controls".
% rightTitle - right panel title, default "Plots".
% topPlotTitle, bottomPlotTitle - dualPlot titles.
% tabs - tabSpec array. Omit for Files + Analysis / Summary + Results / Log.
% spec - scalar struct with fields:
% title - figure title text.
% position - MATLAB figure position [x y width height].
% leftWidth - initial left controls width in pixels.
% options - optional shell options:
% rightKind - "custom" or "dualPlot", default "custom".
% rightGridSize - custom right grid size, default [1 1].
% rightRowHeight - custom right grid rows, default {'1x'}.
% rightRowSpacing - scalar spacing, default 8 or 10 for dualPlot.
% showPlotControls - dualPlot only, default true.
% controlsTitle, rightTitle, topPlotTitle, bottomPlotTitle - labels.
% tabs - labkit.ui.app.tab struct array.
%
% Output:
% ui - struct of figure, layout, tab grids, and right-side handles.
%
% Apps own controls and workflow inside the returned grids. The shell owns
% split-pane layout, tab construction, scrollable tab grids, resizable rows,
% and standard right-pane plumbing.

if nargin < 1 || ~isstruct(spec) || ~isscalar(spec)
error('labkit:ui:InvalidAppShellSpec', ...
'createShell requires a scalar struct spec.');
end
required = {'title', 'position', 'leftWidth'};
for k = 1:numel(required)
if ~isfield(spec, required{k})
error('labkit:ui:InvalidAppShellSpec', ...
'App shell spec is missing "%s".', required{k});
end
end

if nargin < 4
opts = struct();
opts = optionValue(spec, 'options', struct());
if isfield(spec, 'shellOptions')
opts = spec.shellOptions;
end

rightKind = optionValue(opts, 'rightKind', 'custom');
rightKind = char(string(optionValue(opts, 'rightKind', 'custom')));
rightGridSize = optionValue(opts, 'rightGridSize', [1 1]);
rightRowHeight = optionValue(opts, 'rightRowHeight', {'1x'});
rightRowSpacing = optionValue(opts, 'rightRowSpacing', 8);
Expand All @@ -54,7 +68,7 @@
tabSpecs = optionValue(opts, 'tabs', standardTabs());

ui = createTabbedWorkbenchShell( ...
figName, figPosition, leftWidth, shellLabels, tabSpecs, ...
spec.title, spec.position, spec.leftWidth, shellLabels, tabSpecs, ...
rightGridSize, rightRowHeight, rightRowSpacing);

if strcmp(rightKind, 'dualPlot')
Expand All @@ -64,13 +78,13 @@

function tabs = standardTabs()
tabs = [ ...
labkit.ui.tabSpec('filesAnalysis', 'Files + Analysis', [3 1], ...
labkit.ui.app.tab('filesAnalysis', 'Files + Analysis', [3 1], ...
{260, 'fit', 'fit'}, ...
struct('resizeRows', [1 2])), ...
labkit.ui.tabSpec('summaryResults', 'Summary + Results', [2 1], ...
labkit.ui.app.tab('summaryResults', 'Summary + Results', [2 1], ...
{'fit', '1x'}, ...
struct('resizeRows', 1)), ...
labkit.ui.tabSpec('log', 'Log', [1 1], {'1x'})];
labkit.ui.app.tab('log', 'Log', [1 1], {'1x'})];
end

function ui = addDualPlotRegion(ui, opts)
Expand All @@ -85,7 +99,7 @@
ui.topAxes = uiaxes(ui.rightGrid);
ui.topAxes.Layout.Row = 2;
title(ui.topAxes, topTitle);
labkit.ui.enableAxesPopout(ui.topAxes);
labkit.ui.view.draw(ui.topAxes, 'popout');
disableAxesInteractivity(ui.topAxes);

ui.bottomControlsPanel = uipanel(ui.rightGrid, 'Title', bottomTitle);
Expand All @@ -100,20 +114,20 @@
ui.topAxes = uiaxes(ui.rightGrid);
ui.topAxes.Layout.Row = 1;
title(ui.topAxes, topTitle);
labkit.ui.enableAxesPopout(ui.topAxes);
labkit.ui.view.draw(ui.topAxes, 'popout');
disableAxesInteractivity(ui.topAxes);

ui.bottomAxes = uiaxes(ui.rightGrid);
ui.bottomAxes.Layout.Row = 2;
end
title(ui.bottomAxes, bottomTitle);
labkit.ui.enableAxesPopout(ui.bottomAxes);
labkit.ui.view.draw(ui.bottomAxes, 'popout');
disableAxesInteractivity(ui.bottomAxes);
end

function value = optionValue(opts, name, defaultValue)
value = defaultValue;
if isfield(opts, name)
if isstruct(opts) && isfield(opts, name)
value = opts.(name);
end
end
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
function [handled, outputs, debugContext] = dispatchAppRequest(appName, args, nout, handlers)
function [handled, outputs, debugContext] = dispatchRequest(appName, args, nout, handlers)
%DISPATCHAPPREQUEST Dispatch app test/debug launch requests.
%
% Usage:
% [handled, outputs, debug] = labkit.ui.dispatchAppRequest( ...
% [handled, outputs, debug] = labkit.ui.app.dispatchRequest( ...
% "labkit_Example_app", varargin, nargout, handlers);
%
% Inputs:
Expand All @@ -23,7 +23,7 @@
appName = char(appName);
handled = false;
outputs = {};
debugContext = labkit.ui.createDebugContext(appName, struct('enabled', false));
debugContext = labkit.ui.diag.createContext(appName, struct('enabled', false));

if nargin < 4
handlers = struct('command', {}, 'minArgs', {}, ...
Expand All @@ -44,7 +44,7 @@
'%s debug mode returns at most the app figure and debug log.', appName);
end
opts = debugOptions(appName, request, args);
debugContext = labkit.ui.createDebugContext(appName, opts);
debugContext = labkit.ui.diag.createContext(appName, opts);
return;
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%ADDROWRESIZEHANDLE Add a draggable horizontal resize handle between grid rows.
%
% Usage:
% handle = labkit.ui.addRowResizeHandle(fig, grid, 2, ...
% handle = addRowResizeHandle(fig, grid, 2, ...
% struct('topRow', 1, 'bottomRow', 3));
%
% Inputs:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function attachColumnResize(fig, grid, leftColumn, separatorColumn, opts)
%
% Notes:
% This helper mutates layout handles only; apps should normally request
% resizable shells through labkit.ui.createAppShell.
% resizable shells through labkit.ui.app.createShell.

if nargin < 5
opts = struct();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
%CREATETABBEDWORKBENCHSHELL Build the private tabbed workbench skeleton.
%
% Called by:
% labkit.ui.createAppShell
% labkit.ui.app.createShell
%
% Inputs:
% figName - figure name/title.
% figPosition - uifigure Position vector.
% leftWidth - initial fixed width of the left control panel.
% labels - struct with controlsPanel and rightPanel text.
% tabSpecs - struct array from labkit.ui.tabSpec/default shell specs.
% tabSpecs - struct array from labkit.ui.app.tab/default shell specs.
% rightGridSize - right-side uigridlayout size.
% rightRowHeight - right-side grid RowHeight cell array.
% rightRowSpacing - right-side grid RowSpacing scalar.
Expand All @@ -21,7 +21,7 @@
% Notes:
% Logical tab rows are expanded with physical resize-handle rows here.
% App code should place controls through public helpers that call
% labkit.ui.layoutRow rather than depending on physical row indices.
% labkit.ui.view.place rather than depending on physical row indices.

ui = struct();
ui.fig = uifigure('Name', figName, 'Position', figPosition);
Expand Down Expand Up @@ -161,7 +161,7 @@ function enableScrollableGrid(grid)
end
opts.topRow = rowMap(topRow);
opts.bottomRow = rowMap(topRow + 1);
handles(k) = labkit.ui.addRowResizeHandle(fig, grid, rowMap(topRow) + 1, opts);
handles(k) = addRowResizeHandle(fig, grid, rowMap(topRow) + 1, opts);
end
end

Expand Down
12 changes: 6 additions & 6 deletions +labkit/+ui/runWithBusyState.m → +labkit/+ui/+app/runBusy.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
function varargout = runWithBusyState(fig, workFcn, opts)
%RUNWITHBUSYSTATE Run synchronous GUI work with busy feedback.
function varargout = runBusy(fig, workFcn, opts)
%RUNBUSY Run synchronous GUI work with busy feedback.
%
% Usage:
% labkit.ui.runWithBusyState(fig, @() refreshResults(), opts);
% result = labkit.ui.runWithBusyState(fig, @() computeResult(), opts);
% labkit.ui.app.runBusy(fig, @() refreshResults(), opts);
% result = labkit.ui.app.runBusy(fig, @() computeResult(), opts);
%
% Inputs:
% fig - owning uifigure or figure. Empty or invalid figures are accepted;
Expand Down Expand Up @@ -32,14 +32,14 @@
% Notes:
% This helper is intended for long, synchronous callbacks. If the callback
% permanently changes control enable states, refresh those states after
% runWithBusyState returns so the cleanup restore does not preserve stale
% runBusy returns so the cleanup restore does not preserve stale
% pre-run values.

if nargin < 3
opts = struct();
end
if ~isa(workFcn, 'function_handle')
error('labkit:ui:runWithBusyState:InvalidCallback', ...
error('labkit:ui:runBusy:InvalidCallback', ...
'workFcn must be a function handle.');
end

Expand Down
6 changes: 3 additions & 3 deletions +labkit/+ui/tabSpec.m → +labkit/+ui/+app/tab.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
function spec = tabSpec(key, titleText, gridSize, rowHeight, opts)
function spec = tab(key, titleText, gridSize, rowHeight, opts)
%TABSPEC Build a tab specification for the shared workbench shell.
%
% Usage:
% spec = labkit.ui.tabSpec('filesAnalysis', 'Files + Analysis', ...
% spec = labkit.ui.app.tab('filesAnalysis', 'Files + Analysis', ...
% [4 1], {240, 220, 280, 160}, struct('resizeRows', [1 2 3]));
%
% Inputs:
Expand All @@ -19,7 +19,7 @@
% padding, rowSpacing, columnSpacing - grid layout properties.
%
% Output:
% spec - struct consumed by createAppShell spec options tabs.
% spec - struct consumed by createShell spec options tabs.

if nargin < 4 || isempty(rowHeight)
rowHeight = repmat({'fit'}, 1, gridSize(1));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
function debugContext = createDebugContext(appName, opts)
function debugContext = createContext(appName, opts)
%CREATEDEBUGCONTEXT Create an app-neutral debug and trace context.
%
% Usage:
% debug = labkit.ui.createDebugContext("labkit_Example_app", opts);
% debug = labkit.ui.diag.createContext("labkit_Example_app", opts);
% debug.append("Loaded file");
% debug.trace("button pressed");
% debug.trace("scaleBar", "reference changed", "user");
Expand Down Expand Up @@ -95,7 +95,7 @@ function appendTraceToTextLog(line)
if isempty(textArea) || ~isvalid(textArea)
return;
end
labkit.ui.appendLog(textArea, line);
labkit.ui.view.update(textArea, 'appendLog', line);
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
function editor = createAnchorCurveEditor(runtime, imageSize, opts)
%CREATEANCHORCURVEEDITOR Create reusable editable anchor-curve interaction.
function editor = anchorEditor(runtime, imageSize, opts)
%ANCHOREDITOR Create reusable editable anchor-curve interaction.
%
% Usage:
% runtime = labkit.ui.createInteractionRuntime(ax);
% editor = labkit.ui.createAnchorCurveEditor(runtime, size(image), ...
% runtime = labkit.ui.tool.createRuntime(ax);
% editor = labkit.ui.tool.anchorEditor(runtime, size(image), ...
% struct('closed', true, 'style', 'Curve', 'onChanged', @onChanged));
% editor.start(points);
%
% Inputs:
% runtime - interaction runtime returned by labkit.ui.createInteractionRuntime.
% runtime - interaction runtime returned by labkit.ui.tool.createRuntime.
% imageSize - [height width] or image size used for zoom/limit clamping.
% opts - optional struct.
%
Expand Down Expand Up @@ -47,7 +47,7 @@
isa(runtime.axes, 'function_handle') && ...
isfield(runtime, 'createSession') && ...
isa(runtime.createSession, 'function_handle'), ...
'First input must be a labkit.ui.createInteractionRuntime result.');
'First input must be a labkit.ui.tool.createRuntime result.');

state.runtime = runtime;
state.ax = runtime.axes();
Expand Down Expand Up @@ -206,7 +206,7 @@ function deleteEditor()
if ~isempty(state.anchorLine) && isvalid(state.anchorLine)
delete(state.anchorLine);
end
labkit.ui.enableAxesPopout(state.ax);
labkit.ui.view.draw(state.ax, 'popout');
end

function onAxesClicked(~, ~)
Expand Down Expand Up @@ -317,7 +317,7 @@ function ensureGraphics()
created = true;
end
if created
labkit.ui.enableAxesPopout(state.ax);
labkit.ui.view.draw(state.ax, 'popout');
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
function runtime = createImageAxesRuntime(ax, opts)
%CREATEIMAGEAXESRUNTIME Deprecated compatibility runtime for image axes.
function runtime = createRuntime(ax, opts)
%CREATERUNTIME Create a runtime that owns image-axes interaction sessions.
%
% Usage:
% runtime = labkit.ui.createImageAxesRuntime(ax, ...
% runtime = labkit.ui.tool.createRuntime(ax, ...
% struct('figure', fig, 'defaultScrollFcn', @onPreviewScroll));
% session = runtime.createSession(struct( ...
% 'name', 'curveEditor', ...
% 'onPointerDown', @onPointerDown, ...
% 'onScroll', @onScroll, ...
% 'installScrollWheel', true));
%
% New app code should call labkit.ui.createInteractionRuntime. This
% compatibility entry point is retained for one migration cycle.
% The runtime is the public owner for pointer, drag, scroll, and hit-test sessions.

%
% Inputs:
% ax - UI axes used by image tools.
Expand Down Expand Up @@ -122,7 +122,7 @@ function setTraceCallback(fcn)

function installDefaultCallbacks()
if isValidHandle(state.ax)
labkit.ui.enableAxesPopout(state.ax);
labkit.ui.view.draw(state.ax, 'popout');
end
if ~isValidHandle(state.fig) || ~isempty(state.activeScrollToken)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%ADDORINSERTANCHOR Apply anchor insertion policy for createAnchorCurveEditor.
%
% Expected caller:
% labkit.ui.createAnchorCurveEditor when users double-click/add anchors.
% labkit.ui.tool.anchorEditor when users double-click/add anchors.
%
% Inputs:
% points - existing N-by-2 normalized anchor coordinates.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%ANCHORCURVEPOINTS Build displayed anchor path samples for curve editors.
%
% Expected caller:
% labkit.ui.createAnchorCurveEditor and sibling private insertion helpers.
% labkit.ui.tool.anchorEditor and sibling private insertion helpers.
%
% Inputs:
% points - N-by-2 anchor coordinates in image pixel coordinates.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
% field - read-only text field initialized to "-".
% lbl - label handle.

[lbl, field] = labkit.ui.createLabeledEditField(parent, labelText, 'text', ...
[lbl, field] = createLabeledEditField(parent, labelText, 'text', ...
'Editable', 'off', ...
'Value', '-');
lbl.Layout.Row = row;
Expand Down
Loading