eacousineau

GridLayout Axes Bugfix

Aug 15th, 2011
247
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. % Change in GridLayout.ResizeCell() - eacousineau
  2. % Enable non-figure parents in constructor, Parent
  3. classdef GridLayout < handle
  4.    
  5.     properties
  6.         Container
  7.        
  8.         NumRows
  9.         NumCols
  10.         RowHeight
  11.         ColWidth
  12.         HGap
  13.         VGap
  14.         HMargin
  15.         VMargin
  16.        
  17.         % Sizing policies
  18.         % 0: Absolute
  19.         % 1: Proportional
  20.  
  21.         RowHeightPolicy
  22.         RowHeightAbsolute
  23.         RowHeightWeight
  24.        
  25.         ColWidthPolicy
  26.         ColWidthAbsolute
  27.         ColWidthWeight
  28.     end
  29.    
  30.     properties (Constant)
  31.         MinRowHeight = 10
  32.         MinColWidth = 10
  33.     end
  34.    
  35.     properties (SetAccess = private)
  36.         Cell  % Cell container
  37.     end
  38.    
  39.     methods
  40.         function Obj = GridLayout(Parent, varargin)
  41.             Names = { ...
  42.                 'NumRows' ...
  43.                 'NumCols' ...
  44.                 'RowHeight' ...
  45.                 'ColWidth' ...
  46.                 'HGap' ...
  47.                 'VGap' ...
  48.                 'Gap' ...
  49.                 'HMargin' ...
  50.                 'VMargin' ...
  51.                 'Margin'};
  52.             NamesCell = CellProps.GetCellParamNames();
  53.             [P, PC] = ParseArgs(varargin, Names, NamesCell);
  54.            
  55.             if isempty(Parent) % || ~ishghandle(Parent)
  56.                 Parent = gcf;
  57.             end
  58.             Obj.Container = uicontainer( ...
  59.                 'Parent', Parent, ...
  60.                 'ResizeFcn', @(hsrc,ev)UpdateLayout(Obj));
  61.            
  62.             % NumRows
  63.             DefaultNumRows = 1;
  64.             if ~isempty(P.RowHeight) && iscell(P.RowHeight)
  65.                 DefaultNumRows = length(P.RowHeight);
  66.             end
  67.             Obj.NumRows = GetArg(P,'NumRows',DefaultNumRows);
  68.             % NumCols
  69.             DefaultNumCols = 1;
  70.             if ~isempty(P.ColWidth) && iscell(P.ColWidth)
  71.                 DefaultNumCols = length(P.ColWidth);
  72.             end
  73.             Obj.NumCols = GetArg(P,'NumCols',DefaultNumCols);
  74.             % RowHeight
  75.             Value = GetArg(P,'RowHeight','*');
  76.             if ~iscell(Value)
  77.                 Obj.RowHeight = cell(1,Obj.NumRows);
  78.                 [Obj.RowHeight{1:end}] = deal(Value);
  79.             else
  80.                 Obj.RowHeight = P.RowHeight;
  81.             end
  82.             % ColWidth
  83.             Value = GetArg(P,'ColWidth','*');
  84.             if ~iscell(Value)
  85.                 Obj.ColWidth = cell(1,Obj.NumCols);
  86.                 [Obj.ColWidth{1:end}] = deal(Value);
  87.             else
  88.                 Obj.ColWidth = P.ColWidth;
  89.             end
  90.             % HGap
  91.             Obj.HGap = GetArg(P,'HGap',5);
  92.             % VGap
  93.             Obj.VGap = GetArg(P,'VGap',5);
  94.             % Gap (overrides HGap, VGap)
  95.             if ~isempty(P.Gap)
  96.                 Obj.HGap = P.Gap;
  97.                 Obj.VGap = P.Gap;
  98.             end
  99.             % HMargin
  100.             Obj.HMargin = GetArg(P,'HMargin',Obj.HGap);
  101.             % VMargin
  102.             Obj.VMargin = GetArg(P,'VMargin',Obj.VGap);
  103.             % Margin (overrides HMargin, VMargin)
  104.             if ~isempty(P.Margin)
  105.                 Obj.HMargin = P.Margin;
  106.                 Obj.VMargin = P.Margin;
  107.             end
  108.            
  109.             % Create array of cell containers
  110.             Obj.Cell = zeros(Obj.NumRows,Obj.NumCols);
  111.             for RIdx = 1:Obj.NumRows
  112.                 for CIdx = 1:Obj.NumCols
  113.                     ThisCell = uicontainer( ...
  114.                         'Parent', Obj.Container, ...
  115.                         'Units', 'pixels', ...
  116.                         'Position', [5*RIdx 5*CIdx 100 100]); % Not important!
  117.                     Props = CellProps(PC);
  118.                     if ~isempty(Props.Color)
  119.                         set(ThisCell, 'BackgroundColor', Props.Color);
  120.                     end
  121.                     setappdata(ThisCell,'Props',Props);
  122.                     setappdata(ThisCell,'RSpan',[RIdx RIdx]);
  123.                     setappdata(ThisCell,'CSpan',[CIdx CIdx]);
  124.                     Obj.Cell(RIdx,CIdx) = ThisCell;
  125.                 end
  126.             end
  127.  
  128.             % RowHeightPolicy
  129.             Obj.RowHeightPolicy   = zeros(1,Obj.NumRows);
  130.             Obj.RowHeightAbsolute = zeros(1,Obj.NumRows);
  131.             Obj.RowHeightWeight   = zeros(1,Obj.NumRows);
  132.             for RIdx = 1:Obj.NumRows
  133.                 RHeight = Obj.RowHeight{RIdx};
  134.                 if ischar(RHeight)
  135.                     if RHeight(end) == '*'
  136.                         Obj.RowHeightPolicy(RIdx) = 1; % Proportional
  137.                         Weight = RHeight(1:end-1);
  138.                         if isempty(Weight)
  139.                             Weight = '1';
  140.                         end
  141.                         Obj.RowHeightWeight(RIdx) = str2double(Weight);
  142.                     else
  143.                         error('RowHeight must be a string ending in ''*''.')
  144.                     end
  145.                 elseif isnumeric(RHeight)
  146.                     Obj.RowHeightPolicy(RIdx) = 0; % Absolute
  147.                     Obj.RowHeightAbsolute(RIdx) = RHeight;
  148.                 else
  149.                     error('RowHeight must be a string or a number.');
  150.                 end
  151.             end
  152.            
  153.             % ColWidthPolicy
  154.             Obj.ColWidthPolicy   = zeros(1,Obj.NumCols);
  155.             Obj.ColWidthAbsolute = zeros(1,Obj.NumCols);
  156.             Obj.ColWidthWeight   = zeros(1,Obj.NumCols);
  157.             for CIdx = 1:Obj.NumCols
  158.                 CWidth = Obj.ColWidth{CIdx};
  159.                 if ischar(CWidth)
  160.                     if CWidth(end) == '*'
  161.                         Obj.ColWidthPolicy(CIdx) = 1; % Proportional
  162.                         Weight = CWidth(1:end-1);
  163.                         if isempty(Weight)
  164.                             Weight = '1';
  165.                         end
  166.                         Obj.ColWidthWeight(CIdx) = str2double(Weight);
  167.                     elseif any(strcmp(CWidth,{'Auto','a'}))
  168.                         Obj.ColWidthPolicy(CIdx) = 1; % Automatic
  169.                     else
  170.                         error('Illegal ColWidth string format.')
  171.                     end
  172.                 elseif isnumeric(CWidth)
  173.                     Obj.ColWidthPolicy(CIdx) = 0; % Absolute
  174.                     Obj.ColWidthAbsolute(CIdx) = CWidth;
  175.                 else
  176.                     error('ColWidth must be a string or a number.');
  177.                 end
  178.             end
  179.            
  180.             UpdateLayout(Obj);
  181.         end
  182.  
  183.         function MergeCells(Obj, RSpan, CSpan)
  184.             if isempty(RSpan)
  185.                 RSpan = [1 Obj.NumRows];
  186.             end
  187.             if isscalar(RSpan)
  188.                 RSpan = [RSpan RSpan];
  189.             end
  190.             if isempty(CSpan)
  191.                 CSpan = [1 Obj.NumCols];
  192.             end
  193.             if isscalar(CSpan)
  194.                 CSpan = [CSpan CSpan];
  195.             end
  196.             for RIdx = RSpan(1):RSpan(2)
  197.                 for CIdx = CSpan(1):CSpan(2)
  198.                     ThisCell = Obj.Cell(RIdx,CIdx);
  199.                     if RIdx == RSpan(1) && CIdx == CSpan(1)
  200.                         setappdata(ThisCell,'RSpan',RSpan);
  201.                         setappdata(ThisCell,'CSpan',CSpan);
  202.                     else
  203.                         delete(ThisCell);
  204.                     end
  205.                 end
  206.             end
  207.         end
  208.  
  209.         function RemoveCells(Obj, RSpan, CSpan)
  210.             if isempty(RSpan)
  211.                 RSpan = [1 Obj.NumRows];
  212.             end
  213.             if isscalar(RSpan)
  214.                 RSpan = [RSpan RSpan];
  215.             end
  216.             if isempty(CSpan)
  217.                 CSpan = [1 Obj.NumCols];
  218.             end
  219.             if isscalar(CSpan)
  220.                 CSpan = [CSpan CSpan];
  221.             end
  222.             for RIdx = RSpan(1):RSpan(2)
  223.                 for CIdx = CSpan(1):CSpan(2)
  224.                     ThisCell = Obj.Cell(RIdx,CIdx);
  225.                     if ishghandle(ThisCell)
  226.                         delete(ThisCell);
  227.                     end
  228.                 end
  229.             end
  230.         end
  231.  
  232.         function FormatCells(Obj, RSpan, CSpan, varargin)
  233.             if isscalar(RSpan)
  234.                 RSpan = [RSpan RSpan];
  235.             end
  236.             if isscalar(CSpan)
  237.                 CSpan = [CSpan CSpan];
  238.             end
  239.             for RIdx = RSpan(1):RSpan(2)
  240.                 for CIdx = CSpan(1):CSpan(2)
  241.                     ThisCell = Obj.Cell(RIdx,CIdx);
  242.                     if ~ishghandle(ThisCell)
  243.                         continue; % Cell is removed
  244.                     end
  245.                     Props = getappdata(ThisCell,'Props');
  246.                     for i = 1:2:length(varargin)
  247.                         Props.(varargin{i}) = varargin{i+1};
  248.                     end
  249.                     if ~isempty(Props.Color)
  250.                         set(ThisCell, 'BackgroundColor', Props.Color);
  251.                     end
  252.                     setappdata(ThisCell,'Props',Props);
  253.                 end
  254.             end
  255.         end
  256.  
  257.         function UpdateLayout(Obj)
  258.             % Layout size
  259.             Position = getpixelposition(Obj.Container);
  260.             LayoutWidth = Position(3);
  261.             LayoutHeight = Position(4);
  262.  
  263.             % Determine column widths
  264.             CWidthSum = LayoutWidth-(Obj.NumCols-1)*Obj.VGap-2*Obj.VMargin;
  265.             CWidth = zeros(1,Obj.NumCols);
  266.             % The absolute-width columns have priority
  267.             CMask = Obj.ColWidthPolicy == 0;
  268.             CWidth(CMask) = Obj.ColWidthAbsolute(CMask);
  269.             % Divide the remaining space among the proportional rows
  270.             CWidthProportionalMask = Obj.ColWidthPolicy == 1;
  271.             CWidthSumProportional = CWidthSum - sum(CWidth);
  272.             CWidthSumProportional = max(CWidthSumProportional,0);
  273.             CWidthWeight = Obj.ColWidthWeight(CWidthProportionalMask);
  274.             CWidth(CWidthProportionalMask) = CWidthSumProportional*CWidthWeight/sum(CWidthWeight);
  275.             CWidth = max(CWidth, Obj.MinColWidth);
  276.  
  277.             % Determine row heights
  278.             RHeightSum = LayoutHeight-(Obj.NumRows-1)*Obj.HGap-2*Obj.HMargin;
  279.             RHeight = zeros(1,Obj.NumRows);
  280.             % The absolute-height rows have priority
  281.             RMask = Obj.RowHeightPolicy == 0;
  282.             RHeight(RMask) = Obj.RowHeightAbsolute(RMask);
  283.             % Divide the remaining space among the proportional rows
  284.             RMask = Obj.RowHeightPolicy == 1;
  285.             RHeightSumProportional = RHeightSum - sum(RHeight);
  286.             RHeightSumProportional = max(RHeightSumProportional,0);
  287.             Weight = Obj.RowHeightWeight(RMask);
  288.             RHeight(RMask) = RHeightSumProportional*Weight/sum(Weight);
  289.             RHeight = max(RHeight, Obj.MinRowHeight);
  290.            
  291.             % Horizontal and vertical offsets for each cell
  292.             CellOffsetH = cumsum([Obj.VMargin CWidth(1:end-1)+Obj.VGap]);
  293.             CellOffsetV = LayoutHeight-cumsum([RHeight(1)+Obj.HMargin RHeight(2:end)+Obj.HGap]);
  294.  
  295.             % Set cell positions
  296.             Cells = get(Obj.Container,'Children');
  297.             for i = 1:length(Cells)
  298.                 RSpan = getappdata(Cells(i),'RSpan');
  299.                 CSpan = getappdata(Cells(i),'CSpan');
  300.                 RIdx  = RSpan(1):RSpan(2);
  301.                 CIdx  = CSpan(1):CSpan(2);
  302.  
  303.                 set(Cells(i), ...
  304.                     'Position', [ ...
  305.                         CellOffsetH(CIdx(1)) ...
  306.                         CellOffsetV(RIdx(end)) ...
  307.                         sum(CWidth(CIdx))+Obj.HGap*(length(CIdx)-1) ...
  308.                         sum(RHeight(RIdx))+Obj.VGap*(length(RIdx)-1)]);
  309.                    
  310.                 GridLayout.ResizeCell(Cells(i));
  311.             end
  312.         end
  313.     end
  314.    
  315.     methods (Static, Access = private)
  316.         function ResizeCell(Src)
  317.             Child = get(Src,'Children');
  318.             if isempty(Child)
  319.                 return;
  320.             end
  321.             assert(isscalar(Child), ...
  322.                 'Cell has more than one child. This is not allowed.');
  323.            
  324.             IsAxes = false;
  325.             % Use bugfix from layout.GridBagLayout.layout(this)
  326.             % http://www.mathworks.com/matlabcentral/fileexchange/22968-gridbaglayout/
  327.             if ishghandle(Child, 'axes') && ...
  328.                     strcmp(get(Child, 'ActivePositionProperty'), 'outerposition')
  329.                 IsAxes = true;
  330.                 OldUnits = get(Child, 'Units');
  331.                 set(Child, 'Units', 'Pixels');
  332.                 ChildPosition = get(Child, 'OuterPosition');
  333.             else
  334.                 ChildPosition = get(Child, 'Position');
  335.             end
  336.            
  337.             % Child offset and size
  338.             ChildOffset = ChildPosition(1:2);
  339.             ChildSize = ChildPosition(3:4);
  340.             % Cell size
  341.             CellPosition = getpixelposition(Src);
  342.             CellSize = CellPosition(3:4);
  343.             % Cell propoerties
  344.             Props = getappdata(Src,'Props');
  345.             % Horizontal alignment
  346.             HAlign  = Props.HAlign;
  347.             HMargin = Props.HMargin;
  348.             if strcmp(HAlign,'Left')
  349.                 ChildOffset(1) = HMargin;
  350.             elseif strcmp(HAlign,'Right')
  351.                 ChildOffset(1) = CellSize(1)-ChildSize(1)-HMargin;
  352.             elseif strcmp(HAlign,'Center')
  353.                 ChildOffset(1) = (CellSize(1)-ChildSize(1))/2;
  354.             else % Stretch
  355.                 ChildOffset(1) = HMargin;
  356.                 ChildSize(1) = CellSize(1)-2*HMargin;
  357.             end
  358.             % Vertical alignment
  359.             VAlign  = Props.VAlign;
  360.             VMargin = Props.VMargin;
  361.             if strcmp(VAlign,'Bottom')
  362.                 ChildOffset(2) = VMargin;
  363.             elseif strcmp(VAlign,'Top')
  364.                 ChildOffset(2) = CellSize(2)-ChildSize(2)-VMargin;
  365.             elseif strcmp(VAlign,'Center')
  366.                 ChildOffset(2) = (CellSize(2)-ChildSize(2))/2;
  367.             else % Stretch
  368.                 ChildOffset(2) = VMargin;
  369.                 ChildSize(2) = CellSize(2)-2*VMargin;
  370.             end
  371.             % Prevent non-positive sizes
  372.             ChildSize = max(ChildSize,.1);
  373.             % Update child position
  374.             ChildPosition = [ChildOffset ChildSize];
  375.             if ~IsAxes
  376.                 set(Child,'Position', ChildPosition);
  377.             else
  378.                 set(Child, 'OuterPosition', ChildPosition, 'Units', OldUnits);
  379.             end
  380.         end
  381.        
  382.     end
  383. end
  384.  
  385. % EOF
RAW Paste Data