%% elementary layout building block, positions matrix of elements
classdef RE_Box < hgsetget
properties
Position = []; % box position
Handle = []; % array of handles
Element = {[]}; % elements matrix HxW
Width = -1; % vector Wx1
Height = -1; % vector Hx1
Padding = [0, 0, 0, 0]; % [left bottom right top]
Spacing = [0, 0]; % [horizontal, vertical]
HPopup = 21; % popup height on win classic/xp
end
properties (Dependent = true)
Children;
Extent;
end
methods
% CONSTRUCTOR
function obj = RE_Box(element)
if ~nargin, element = []; end
set(obj, 'Element', element);
end
function obj = set.Element(obj, element)
if iscell(element)
ind = cellfun(@(x) isnumeric(x) && ~isempty(x), element(:));
hcell = element(ind);
harray = [hcell{:}];
elseif isnumeric(element)
harray = element;
end
obj.Handle = harray(:);
obj.Element = element;
end
function obj = set.Padding(obj, padding)
N = length(padding);
if N == 1, padding = padding(ones(1,4));
elseif N == 2, padding = padding([1,2,1,2]); end
obj.Padding = padding;
end
function obj = set.Spacing(obj, spacing)
if length(spacing) == 1, spacing = spacing([1,1]); end
obj.Spacing = spacing;
end
function obj = set.Position(obj, position)
obj.Position = position;
setPosition(obj);
end
function extent = get.Extent(obj)
extent = getExtent(obj);
end
function children = get.Children(obj)
children = getChildren(obj);
end
end % methods
methods (Access = protected)
%% calculate object extent
function extent = getExtent(obj)
extent = [-1, -1];
if all(obj.Width > 0)
extent(1) = sum(obj.Width) + sum(obj.Padding([1,3])) + ...
obj.Spacing(1)*(length(obj.Width) - 1);
end
if all(obj.Height > 0)
extent(2) = sum(obj.Height) + sum(obj.Padding([2,4])) + ...
obj.Spacing(2)*(length(obj.Height) - 1);
end
end
%% recursively get handles off all children controls
function children = getChildren(obj)
children = obj.Handle;
if ~iscell(obj.Element), return; end;
ind = cellfun(@(x) ~isnumeric(x) && ~isempty(x), obj.Element(:));
if ~any(ind), return; end
elements = obj.Element(ind);
for i = elements(:)'
children = [children; i{1}.Children];
end
end
%% set box position
function setPosition(obj)
position = obj.Position;
sw = position(3) - obj.Padding(1) - obj.Padding(3);
sh = position(4) - obj.Padding(2) - obj.Padding(4);
rh = position(2) + position(4) - obj.Padding(4);
s2 = length(obj.Width);
s1 = length(obj.Height);
if s2 == 1 && obj.Width > 0, sw = obj.Width; end
if s1 == 1 && obj.Height > 0, sh = obj.Height; end
if s2 > 1, sw = local_size(obj.Width, sw - (s2-1)*obj.Spacing(1)); end
if s1 > 1, sh = local_size(obj.Height, sh - (s1-1)*obj.Spacing(2)); end
for ih = 1:s1
rh = rh - sh(ih);
rw = position(1) + obj.Padding(1);
for iw = 1:s2
if iscell(obj.Element)
iElement = obj.Element{ih, iw};
else
iElement = obj.Element(ih, iw);
end
if ~isempty(iElement)
pos = [rw, rh, sw(iw), sh(ih)];
% hack to allign text + editfield + button
if isnumeric(iElement) && sh(ih) == obj.HPopup && s2 > 1 && strcmp('uicontrol', get(iElement,'type'))
style = get(iElement, 'style');
switch style
case 'text'
pos(2) = pos(2) - 4;
case {'togglebutton', 'pushbutton'}
pos(2) = pos(2) - 1;
pos(4) = pos(4) + 2;
end
end
set(iElement, 'Position', pos);
end
rw = rw + sw(iw) + obj.Spacing(1);
end
rh = rh - obj.Spacing(2);
end
end
end % methods
end % classdef
%% calculate size of box elements
function sz = local_size(given, available)
sz = given; % sizes
ip = sz > 0; % indices of fixed sizes
fs = sum(sz(ip)); % sum of fixed sizes
rs = max(available - fs, 1); % rest sizes (sum of dynamic sizes)
ns = sz(~ip); % negative (dynamic) sizes
sz(~ip) = ns * (rs/sum(ns)); % re-assign negative sizes (turned positive)
if sum(sz) < available + 3, return; end
sz = sz*available/sum(sz); % normalize sizes
sz = max(1,abs(sz)); % make sure size is always positive
end