overlapping images in grid layout

28 views (last 30 days)
Rich006
Rich006 on 22 Sep 2023
Commented: Adam Danz on 6 Oct 2023
I am trying to create a solitaire card game app (think FreeCell) using App Designer. To graphically display the tableau, my idea is to represent the cards as images in a Grid Layout, which would nicely handle the user resizing the app window. However, the grid cells need to be shorter than the card images, as some cards need to partially overlap others (the image shows some notional grid cells outlined in cyan). More to the point, in order for the top (lowest on screen) card to be fully visible, the card images need to be able to spill out below their cells. I do not see a way to allow that in App Designer's grid layout. Is that possible, or is there a better way to display the overlapping card images?
What I have so far (in the screenshot) is a one-row grid for the bottom cards (those that are highest in the app window) and the other cards are not on a grid. This works perfectly as long as the window is not resized.

Accepted Answer

Adam Danz
Adam Danz on 22 Sep 2023
Edited: Adam Danz on 22 Sep 2023
I would suggest a different approach. You could potentially have dozens of axes and just as many images in your app which could result in sluggish responses of the app. You'd also need to manage the uistack to ensure that various graphics objects are stacked in the correct order. It sounds like it could be a mess.
Are you stuck to using images? The cards could be simulated using text and rectangles within a single axes.
Here's a demo to get you started. At the end, I'll show how to work with these objects once they are plotted.
Create card strings, a modification of @Toshiaki Takeuchi's 2020 blog article
deck = ["A"; (2:10)';'J';'Q';'K'] + ["♥","♦","♠","♣"]
deck = 13×4 string array
"A♥" "A♦" "A♠" "A♣" "2♥" "2♦" "2♠" "2♣" "3♥" "3♦" "3♠" "3♣" "4♥" "4♦" "4♠" "4♣" "5♥" "5♦" "5♠" "5♣" "6♥" "6♦" "6♠" "6♣" "7♥" "7♦" "7♠" "7♣" "8♥" "8♦" "8♠" "8♣" "9♥" "9♦" "9♠" "9♣" "10♥" "10♦" "10♠" "10♣" "J♥" "J♦" "J♠" "J♣" "Q♥" "Q♦" "Q♠" "Q♣" "K♥" "K♦" "K♠" "K♣"
deckColors = repelem(["r","r","k","k"],height(deck),1)
deckColors = 13×4 string array
"r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k" "r" "r" "k" "k"
Specify number of cards in each stack
For simplicity, the number of cards are hard-coded in this demo.
% Indicate the number of cards in each stack
nCards = [5 2 4 6 4]; % number of cards in each stack
nStacks = numel(nCards); % number of vertical stacks
Compute card size and position
% Compute the x-coordinates of the left edge of each stack
% where all cards are within [0,1] x-limits.
gap = 0.05; % horizontal gap between stacks
cardWidth = ((1-2*gap)-(gap*(nStacks-1)))/nStacks;
xStack = gap:(cardWidth+gap):(1-gap);
% Compute card height
cardHeight = 1.4286 * cardWidth; % based on actual playing card dimensions
% Specify the vertical shift - how much of each card will show (except the
% bottom ones)
yShift = 0.3; % Percentage of card-height
Randomly select cards
This uses randperm to avoid duplicates
% Select a random set of cards without duplicates
randIdx = randperm(numel(deck),sum(nCards));
randCards = deck(randIdx);
randCardColors = deckColors(randIdx);
Plot the cards in an axes
The demo creates a new figure and axes but you can replace this with your app's figure and axes handles.
% Create the cards in an axes
fig = figure('Color',[0 .5 0]);
ax = axes(fig,'Position',[0 0 1 1]);
xlim(ax,[0,1])
ylim(ax,[-inf,0]);
hold(ax,'on')
% Loop through each stack
cardObjs = gobjects(1,sum(nCards));
for i = 1:nStacks
% Compute y vertical position of bottom edge of all cards in this stack
% from the top card to the bottom card.Note that the top edge of the
% axes will be 0 and the bottom edge will be some negative value.
yStack = (-gap-cardHeight) : -(yShift*cardHeight) : (-gap-cardHeight)-(nCards(i)*(yShift*cardHeight));
% Loop through each card
for j = 1:nCards(i)
n = sum(nCards(1:(i-1)))+j; % n^th total card
cardObjs(n) = hggroup(ax);
% Card rectangle
cardPosition = [xStack(i), yStack(j), cardWidth, cardHeight];
rectangle('parent',cardObjs(n), ...
'Position', cardPosition, ...
'Curvature', 0.3, ...
'FaceColor','w');
% Card face (text), position is at top, middle of card
text('parent',cardObjs(n), ...
'String', randCards(n), ...
'Color', randCardColors(n), ...
'FontSize', 18, ... % adjust as needed or better yet, compute it
'Position', [cardPosition(1)+cardWidth/2, cardPosition(2)+cardHeight, 0],...
'HorizontalAlignment', 'Center', ...
'VerticalAlignment', 'top');
end
end
% Tidy up the plot
axis equal off
Now that some cards exist, you can work with them using their handles which are Group objects created by hggroup, stored in variable cardObjs.
Each group contains a rectangle and text object. You can delete the k^th group using delete(cardObjs(k)).
Change the position of a card by updating the Position properties of each child of the group.
cardObjs(k).Children(1).Position % text
cardObjs(k).Children(2).Position % rectangle
Since the cards will change position frequently, you could write a helper function to do this. You could also add a listener to the text object in each group so that it follows the rectangle. That way you'd only need to update the position of the rectange.
As you can probably imagine, there's a lot of flexibility here and with that, I'll hand it off to you. Good luck!
  7 Comments
Rich006
Rich006 on 6 Oct 2023
Another note: the Graphics objects are always centered in the Axes, which means the vertical placement of the top cards depends on how many cards are in the longest stack. In order to keep the position consistent regardless of how many cards are in the longest stack, I added a ghost Graphics object at the position of the bottom of the longest possible stack that can occur in the game.
Adam Danz
Adam Danz on 6 Oct 2023
Hi Rich006, that sounds reasonable. You could also change how yStack and the axis limits are computed. Sounds like you're in control of it 😊

Sign in to comment.

More Answers (0)

Categories

Find more on Measurements and Spatial Audio in Help Center and File Exchange

Products


Release

R2023a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!