Skip to content

11. Stack select

RhettTR edited this page Feb 20, 2024 · 2 revisions

Movement and selection of stacks/counters have been implemented. With this the base functionality of the engine is done.

A run down of the features:

Hoover - opens a window with a tiny image of the map below the counter(s); counters are shown in 100% scale; window deleted when you move the cursor (or hit ESC).

Left-click - selects the counter(s); all previous selections are undone.

SHIFT Left-click - adds to the current selection (previous selections are not undone).

Left-click non-counter - deletes all selections.

Double Left-click - opens a window with images of counters in stack order; in this window you can select/unselect counters and drag a counter to a new position (reorder); window is deleted when double left-clicking again on the stack (or hitting ESC).

Drag Left-click - on unselected counter(s) will move them to a new position (no selection is done); on one or more selected counter(s) will drag all selected counters (also those in other stacks) to a new position.

Right-click - will open a menu of actions that can be applied to this counter only.


A selection looks like this.

selection

A selection draws a rectangle around the counter, here colored in red. The user should be able to chose a selection color. Note that the rectangle is drawn underneath the Moved-marker.

In Qt it is not possible to paint outside the surface of a widget. In order to paint a selection (and the Moved-marker) the widget needs to be expanded with a margin. The margin width is set at 9 pixels. The margin must be symmetric (have the same width all around the counter).

state

The position of the counter (its State coordinates) is the upper left corner of the counter including its margin.


A stack of counters is moved as a whole, that is to say that each counter in the stack is moved the same distance. Multiple stacks are also moved as a whole if all the counters are selected. It is not more difficult to implement multi-stack movement than single-stack movement because the basic functionality is the same.

When dragging stacks, an image of the dragged stacks is necessary as a visual feed-back. This image can be called a ghost image. The ghost image is a QPixmap generated from the counters. The code looks like this:

QImage CentralFrame::selectedGhostImage(QRect &totalRect)
{
    
    
    // generate stacks of selected

    for (auto obj = Counter::counters.begin(); obj != Counter::counters.end(); ++obj)
    {
	    
	    if (obj->second->selected == true)
	    {
		    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;
		    }
	    }
		    
    }
    
    
    
    
    // find size of ghost
    
    
    for (auto const& [point, stack] : stacks)	
    {	
	    
	    for ( auto obj = stack.begin(); obj != stack.end(); ++obj )
	    {		
			    
		    int x = obj->second->counter->pos().x();
		    int y = obj->second->counter->pos().y();
		    
		    
		    QPoint p = QPoint(x, y);
		    
		    QRect thisRect = QRect(p, obj->second->counter->size());
		    
		    totalRect = totalRect.united(thisRect);
		    
	    }
	    
    }
    
    
    
    // paint ghost
    
    QImage base = QImage (totalRect.size(), QImage::Format_ARGB32_Premultiplied);
	    
    base.fill(Qt::transparent);
    
    QPainter *paint = new QPainter(&base);
    
	    
    for (auto const& [point, stack] : stacks)	
    {
    
	    for ( auto obj = stack.begin(); obj != stack.end(); ++obj )
	    {			
		    
		    // coordinates relative to paint surface
		    int x = obj->second->counter->pos().x() - totalRect.x();
		    int y = obj->second->counter->pos().y() - totalRect.y();
		    
			    
		    paint->drawImage(x, y, obj->second->baseBuffer);
		    
	    }
    
    }
    
    delete paint;

    
    
    // make transparent
    QImage transparent = QImage (totalRect.size(), QImage::Format_ARGB32_Premultiplied);
    transparent.fill(Qt::transparent);
    paint = new QPainter(&transparent);
    paint->setOpacity(0.5);
    paint->drawImage(0, 0, base);
    delete paint;
    
    
    
    
    return transparent;

}

Since no data structure holds selected counters, the stacks of selected counters must first be generated.

Then the size of the ghost image must be found, what can be called the ghostRect. This is done by uniting the rects of the individual counters. QRect has a function called united for this. The ghostRect is the minimum area of all the counters including their margins.

ghost

The ghost image is then painted with the images of the individual counters. Finally the whole ghost image is made semi-transparent.


Hoovering over a stack looks like this. The cursor must not move for at least 0,7 seconds.

hoover

Two selected stacks are moved. Start position:

start

Dragging of ghost image:

move

Drag drop and final position (all counters marked as moved):

end


Here is a sequence of opening, reordering, unselecting and then moving the selected units.

Open the stack:

open

Unselect top and bottom counter:

unselect

Reorder (swap) the top and bottom counter of the stack:

reorder

Reorder (swap) blue French units:

swap

Hoover over new stack:

new

Move selected units of the stack:

move

Hoover over the moved stack:

moved


Code found here.


CPLUS_INCLUDE_PATH=/opt/Qt6.5/include;export CPLUS_INCLUDE_PATH

LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/opt/Qt6.5/lib;export LD_LIBRARY_PATH

g++ -Wall -o main main.cpp luau.cpp counter.cpp window.cpp frame.cpp overlay.cpp -I/home/me/luau/VM/include -I/home/me/luau/Compiler/include -I/opt/Qt6.5/include/QtWidgets -Wl,--copy-dt-needed-entries -L/opt/Qt6.5/lib -lQt6Core -lQt6Widgets -L/home/me/luau/build/release -lluauvm -lluaucompiler -lluauast -lisocline

Clone this wiki locally