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
67 changes: 4 additions & 63 deletions +labkit/+ui/+app/createShell.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,18 @@
% 'title', "Example", ...
% 'position', [90 70 1200 800], ...
% 'leftWidth', 360, ...
% 'options', struct('rightKind', 'dualPlot')));
% 'options', struct('rightGridSize', [1 1])));
%
% Inputs:
% 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.
% rightRowSpacing - scalar right-grid spacing, default 8.
% rightTitle - right panel label.
% tabs - labkit.ui.app.tab struct array.
%
% Output:
Expand All @@ -42,38 +40,19 @@
end

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

rightKind = char(string(optionValue(opts, 'rightKind', 'custom')));
rightGridSize = optionValue(opts, 'rightGridSize', [1 1]);
rightRowHeight = optionValue(opts, 'rightRowHeight', {'1x'});
rightRowSpacing = optionValue(opts, 'rightRowSpacing', 8);
if strcmp(rightKind, 'dualPlot')
showPlotControls = optionValue(opts, 'showPlotControls', true);
if showPlotControls
rightGridSize = [4 1];
rightRowHeight = {'fit', '1x', 'fit', '1x'};
else
rightGridSize = [2 1];
rightRowHeight = {'1x', '1x'};
end
rightRowSpacing = optionValue(opts, 'rightRowSpacing', 10);
end

shellLabels = struct( ...
'controlsPanel', optionValue(opts, 'controlsTitle', 'Controls'), ...
'controlsPanel', 'Controls', ...
'rightPanel', optionValue(opts, 'rightTitle', 'Plots'));
tabSpecs = optionValue(opts, 'tabs', standardTabs());

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

if strcmp(rightKind, 'dualPlot')
ui = addDualPlotRegion(ui, opts);
end
end

function tabs = standardTabs()
Expand All @@ -87,44 +66,6 @@
labkit.ui.app.tab('log', 'Log', [1 1], {'1x'})];
end

function ui = addDualPlotRegion(ui, opts)
topTitle = optionValue(opts, 'topPlotTitle', 'Top Plot');
bottomTitle = optionValue(opts, 'bottomPlotTitle', 'Bottom Plot');
showPlotControls = optionValue(opts, 'showPlotControls', true);

if showPlotControls
ui.topControlsPanel = uipanel(ui.rightGrid, 'Title', topTitle);
ui.topControlsPanel.Layout.Row = 1;

ui.topAxes = uiaxes(ui.rightGrid);
ui.topAxes.Layout.Row = 2;
title(ui.topAxes, topTitle);
labkit.ui.view.draw(ui.topAxes, 'popout');
disableAxesInteractivity(ui.topAxes);

ui.bottomControlsPanel = uipanel(ui.rightGrid, 'Title', bottomTitle);
ui.bottomControlsPanel.Layout.Row = 3;

ui.bottomAxes = uiaxes(ui.rightGrid);
ui.bottomAxes.Layout.Row = 4;
else
ui.topControlsPanel = [];
ui.bottomControlsPanel = [];

ui.topAxes = uiaxes(ui.rightGrid);
ui.topAxes.Layout.Row = 1;
title(ui.topAxes, topTitle);
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.view.draw(ui.bottomAxes, 'popout');
disableAxesInteractivity(ui.bottomAxes);
end

function value = optionValue(opts, name, defaultValue)
value = defaultValue;
if isstruct(opts) && isfield(opts, name)
Expand Down
31 changes: 0 additions & 31 deletions +labkit/+ui/+app/private/disableAxesInteractivity.m

This file was deleted.

25 changes: 3 additions & 22 deletions +labkit/+ui/+view/draw.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@
% App-facing contract:
% labkit.ui.view.draw(ax, "reset", titleText, resetScaleAndTicks)
% hImage = labkit.ui.view.draw(ax, "image", imageData, titleText, opts)
% info = labkit.ui.view.draw(ax, "xy", x, y, labels, opts)
% labkit.ui.view.draw(ax, "clear")
% labkit.ui.view.draw(ax, "popout")
%
% Inputs:
% ax - target axes.
% action - "reset", "image", "xy", "clear", or "popout".
% action - "reset", "image", "clear", or "popout".
% varargin - action-specific payload described above.
%
% Outputs:
% image returns the image graphics object. xy returns a status struct.
% reset, clear, and popout mutate axes in place and return [] when captured.
% image returns the image graphics object. reset, clear, and popout mutate
% axes in place and return [] when captured.

switch normalizeAction(action)
case 'reset'
Expand All @@ -28,12 +27,6 @@
titleText = positional(varargin, 2, '');
opts = positional(varargin, 3, struct());
out = showImage(ax, imageData, titleText, opts);
case 'xy'
x = positional(varargin, 1, []);
y = positional(varargin, 2, []);
labels = positional(varargin, 3, struct());
opts = positional(varargin, 4, struct());
out = plotXY(ax, x, y, labels, opts);
case 'clear'
clearAxes(ax);
out = [];
Expand All @@ -52,18 +45,6 @@

function action = normalizeAction(action)
action = lower(regexprep(char(string(action)), '[^a-zA-Z0-9]', ''));
switch action
case {'plot', 'plotxy'}
action = 'xy';
case {'imageaxes', 'showimage'}
action = 'image';
case {'resetaxes', 'hardreset'}
action = 'reset';
case {'clearaxes'}
action = 'clear';
case {'enablepopout', 'axespopout'}
action = 'popout';
end
end

function value = positional(args, index, defaultValue)
Expand Down
62 changes: 14 additions & 48 deletions +labkit/+ui/+view/form.m
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
function varargout = form(parent, spec, varargin)
function varargout = form(parent, spec)
%FORM Create LabKit form controls from a unified control spec.
%
% Usage:
% [lbl, spinner] = labkit.ui.view.form(parent, struct( ...
% 'kind', 'spinner', 'label', 'Samples:', ...
% 'value', 10, 'limits', [1 Inf], 'step', 1, ...
% 'callback', @onChanged));
% [lbl, spinner] = labkit.ui.view.form(parent, 'spinner', 'Samples:', ...
% 'Value', 10, 'Limits', [1 Inf], 'ValueChangedFcn', @onChanged);
%
% ui = labkit.ui.view.form(parent, struct( ...
% 'title', 'Settings', 'row', 2, 'layout', [2 2], ...
% 'controls', [struct('id','mode','kind','dropdown', ...)]));
%
% Inputs:
% parent - parent grid or shell tab grid.
% spec - scalar struct or control kind. Single-control specs use
% kind/label fields. Section specs use title, row, layout, and
% controls fields. String kind calls are normalized into a control
% spec and are the preferred app-facing replacement for one-off
% labeled control helper calls.
% spec - scalar struct. Single-control specs use kind/label fields. Section
% specs use title, row, layout, and controls fields.
%
% Control fields:
% id - optional valid field name for returned ui.controls.(id).
% kind - "spinner", "dropdown", "edit", "readonly", "info", "button",
% or "checkbox".
% label - label text for labeled controls.
% value, items, limits, step, enabled, callback - optional common values.
% args - optional cell array of raw MATLAB name/value pairs.
% style - edit-field style, default "text".
% value, items, limits, step, valueDisplayFormat, enabled, callback -
% optional common values.
% text - button or checkbox label text.
% row, column - optional layout location.
%
% Outputs:
% Single-control call returns [labelHandle, controlHandle] for labeled
Expand All @@ -40,16 +38,12 @@

if nargin < 2
error('labkit:ui:view:InvalidFormSpec', ...
'form requires a scalar struct spec or control kind.');
end

if ~isstruct(spec)
spec = shorthandSpec(spec, varargin);
'form requires a scalar struct spec.');
end

if ~isstruct(spec) || ~isscalar(spec)
error('labkit:ui:view:InvalidFormSpec', ...
'form requires a scalar struct spec or control kind.');
'form requires a scalar struct spec.');
end

if isfield(spec, 'controls')
Expand All @@ -68,36 +62,6 @@
end
end

function spec = shorthandSpec(kind, args)
kind = lower(char(string(kind)));
switch kind
case {'spinner', 'dropdown'}
if isempty(args)
error('labkit:ui:view:InvalidFormSpec', ...
'%s controls require label text.', kind);
end
spec = struct('kind', kind, 'label', args{1}, 'args', {args(2:end)});
case 'edit'
if numel(args) < 2
error('labkit:ui:view:InvalidFormSpec', ...
'edit controls require label text and edit style.');
end
spec = struct('kind', 'edit', 'label', args{1}, ...
'style', args{2}, 'args', {args(3:end)});
case 'readonly'
spec = struct('kind', 'readonly', 'args', {args});
case 'info'
if numel(args) < 2
error('labkit:ui:view:InvalidFormSpec', ...
'info rows require row and label text.');
end
spec = struct('kind', 'info', 'row', args{1}, 'label', args{2});
otherwise
error('labkit:ui:view:UnknownControlKind', ...
'Unsupported form control kind "%s".', kind);
end
end

function ui = createFormSection(parent, spec)
layout = optionValue(spec, 'layout', [numel(spec.controls) 2]);
ui = labkit.ui.view.section(parent, ...
Expand Down Expand Up @@ -161,8 +125,7 @@ function setValue(id, value, reason)
function [lbl, ctrl] = createOne(parent, spec)
kind = lower(char(string(optionValue(spec, 'kind', 'edit'))));
labelText = char(string(optionValue(spec, 'label', '')));
args = optionValue(spec, 'args', {});
args = [commonArgs(spec), args];
args = commonArgs(spec);

switch kind
case 'spinner'
Expand Down Expand Up @@ -223,6 +186,9 @@ function setValue(id, value, reason)
if isfield(spec, 'step')
args = [args, {'Step', spec.step}];
end
if isfield(spec, 'valueDisplayFormat')
args = [args, {'ValueDisplayFormat', spec.valueDisplayFormat}];
end
if isfield(spec, 'enabled')
args = [args, {'Enable', onOff(spec.enabled)}];
end
Expand Down
43 changes: 2 additions & 41 deletions +labkit/+ui/+view/panel.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
% ui = labkit.ui.view.panel(parent, spec)
%
% Inputs:
% parent - parent container for the component group. For top/bottom plot
% controls this is the top controls panel.
% kind - component kind: "files", "log", "text", "table",
% "plotOptions", or "topBottomPlotControls".
% parent - parent container for the component group.
% kind - component kind: "files", "log", "text", or "table".
% spec - optional struct alternative with kind plus fields matching the
% positional arguments described below.
%
Expand All @@ -18,9 +16,6 @@
% panel(parent, "log", row, initialValue)
% panel(parent, "text", title, row, lines, opts)
% panel(parent, "table", title, row, columnNames, initialData)
% panel(parent, "plotOptions", rowCount, row)
% panel(topPanel, "topBottomPlotControls", bottomPanel, xItems, yItems,
% topDefaults, bottomDefaults, valueChangedFcn)
%
% Output:
% ui - struct of MATLAB component handles owned by the created component.
Expand Down Expand Up @@ -52,19 +47,6 @@
columnNames = positional(varargin, 3, {});
initialData = positional(varargin, 4, cell(0, numel(columnNames)));
ui = resultTable(parent, titleText, row, columnNames, initialData);
case 'plotoptions'
rowCount = positional(varargin, 1, 1);
row = positional(varargin, 2, 3);
ui = plotOptionsPanel(parent, rowCount, row);
case 'topbottomplotcontrols'
bottomPanel = positional(varargin, 1, []);
xItems = positional(varargin, 2, {});
yItems = positional(varargin, 3, {});
topDefaults = positional(varargin, 4, struct());
bottomDefaults = positional(varargin, 5, struct());
valueChangedFcn = positional(varargin, 6, []);
ui = topBottomPlotControls(parent, bottomPanel, xItems, yItems, ...
topDefaults, bottomDefaults, valueChangedFcn);
otherwise
error('labkit_ui:panel:UnknownKind', ...
'Unknown LabKit view panel kind "%s".', char(kind));
Expand All @@ -91,15 +73,6 @@
ui = resultTable(parent, fieldOr(spec, 'title', ''), ...
fieldOr(spec, 'row', 1), columnNames, ...
fieldOr(spec, 'initialData', cell(0, numel(columnNames))));
case 'plotoptions'
ui = plotOptionsPanel(parent, fieldOr(spec, 'rowCount', 1), ...
fieldOr(spec, 'row', 3));
case 'topbottomplotcontrols'
ui = topBottomPlotControls(parent, requireField(spec, 'bottomPanel'), ...
fieldOr(spec, 'xItems', {}), fieldOr(spec, 'yItems', {}), ...
fieldOr(spec, 'topDefaults', struct()), ...
fieldOr(spec, 'bottomDefaults', struct()), ...
fieldOr(spec, 'callback', []));
otherwise
error('labkit_ui:panel:UnknownKind', ...
'Unknown LabKit view panel kind "%s".', char(kind));
Expand All @@ -108,18 +81,6 @@

function key = normalizeKind(kind)
key = lower(regexprep(char(string(kind)), '[^a-zA-Z0-9]', ''));
switch key
case {'file', 'fileselection', 'filepanel'}
key = 'files';
case {'readonlytext', 'textpanel'}
key = 'text';
case {'result', 'results', 'resulttable', 'tablepanel'}
key = 'table';
case {'plotoption', 'plotoptionspanel'}
key = 'plotoptions';
case {'topbottom', 'topbottomplots', 'topbottomplot'}
key = 'topbottomplotcontrols';
end
end

function value = positional(args, index, defaultValue)
Expand Down
Loading
Loading