The repository is where the counters/cards/markers of a game are stored. From the repository you drag and copy them onto the map.
The repository is a separate window. The advantage of a separate window is that you can minimize it when it is not needed. It makes the use of the screen more efficient.
The first repository window is simple. It has 4 counters organized in 2 rows with 3 counters in the first row.

It is now possible to drag counters to the map window.

The repository window will later include tabs, sub-tabs and subsub-tabs to make the organization of the game's counters/cards/markers as easy and accessible as possible.
The contents of the repository window is generated by the Lua script. It is necessary with a strict separation between the counters in the repository and the counters on the map. A table called Repository holds the repository while the table called Counters stores the counters on the map.
-- create four counters in counter window
Repository["12R-51R"] = Repository:new({"12R-51R", "12R-51R_f"})
Repository["BRIGADE-3"] = Repository:new({"BRIGADE-3", "BRIGADE-3_f"})
Repository["38-94"] = Repository:new({"38-94", "38-94_f"})
Repository["VII-40"] = Repository:new({"VII-40", "VII-40_f"})
Regarding stacking, it was first necessary to make a default grid or default snap-to functionality. The reason for such a default grid is that counters placed very close together should be considered occupying the same x-y-location. Anything else would confuse players. A default grid is also useful for making stacks of counters when the map has no other kind of snap-to functionality.
void Counter::snaptoDefaultGrid (Counter *counter, int *x, int *y)
{
// find the first (if any) counter close enough to snap to
int maxDistance = 15;
for ( auto obj = counters.begin(); obj != counters.end(); ++obj )
if (counter != obj->second)
{
if (abs(*x - obj->second->state.x) < maxDistance &&
abs(*y - obj->second->state.y) < maxDistance)
{
*x = obj->second->state.x;
*y = obj->second->state.y;
return;
}
}
}
The distance must be less than 15 pixels to be considered the same point.
Another issue is the ability to cancel a drag, or in other words to consider a drag less than a small distance as not a move.
if (event->source() == this)
{
int minimumMovement = 4;
if (abs(dragged->owner->state.x - dx) > minimumMovement ||
abs(dragged->owner->state.y - dy) > minimumMovement)
{
dragged->owner->state.x = dx;
dragged->owner->state.y = dy;
Luau::doEvent("movetrigger", dragged->owner->name.c_str(), "", "", 0);
Luau::doEvent("move", dragged->owner->name.c_str(), "Counter", "x", dx);
Luau::doEvent("move", dragged->owner->name.c_str(), "Counter", "y", dy);
Luau::doEvent("end", "", "", "", 0);
}
dragged->owner->zorder = Counter::topZorder();
dragged->owner->setGUI();
dragged->raise(); // NB!!
}
This code ensures that only drops greater than 4 pixels away from the start position are considered moves. maxDistance > minimumMovement because it must be easier to stack counters. A wrong move can in any case just be undone.
The main point concerning stacking is that the data structure holding stacks is generated every time it is needed, i.e. any time the map needs repainting.
This point is very important. It saves maintaining complex data structures that need updating every time a counter or a stack of counters is moved. It makes the code simpler, smaller and less prone to bugs.
The downside is the CPU time used. Modern computers are fast enough for this, especially when the total number of counters/cards on the map is in the magnitude of a thousand (n=1000).
Stacks are always generated from the same basic data structure, the std::map holding the counters.
struct point
{
int x;
int y;
bool operator <(const point &p) const
{
return (x < p.x) || (!(p.x < x) && y < p.y);
}
};
typedef struct point Point;
std::map<Point, Stack> stacks;
for ( auto obj = counters.begin(); obj != counters.end(); ++obj )
{
Point p = (Point){.x = obj->second->state.x, .y = obj->second->state.y};
if (stacks.find( p ) == stacks.end())
{
// not found
Stack stack = {{obj->second->zorder, obj->second}};
stacks[p] = stack;
}
else
{
// found
Stack stack = stacks.at( p );
stack[obj->second->zorder] = obj->second;
stacks[p] = stack;
}
}
// render all "stacks" with 1 or more counters
for (auto const& [point, stack] : stacks)
setStack(stack);
There are two useful ways of displaying stacks, either with an offset or with no offset and a number telling the size > 1 of the stack.


Stacks with no offset can be useful in cases where tall stacks are placed close together.
A boolean decides what stack-type is used.
bool Counter::haveOffset = false;
Later this will be an entry in a preference box for the user to decide. Likewise, if there is an offset then the user should be able to set the number of offset-pixels.
int Counter::stackOffset = 5;
The complete code for this section is found [link to old code deleted].