88 lines
3.6 KiB
C++
88 lines
3.6 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
|
|
#include <QRect>
|
|
#include <QString>
|
|
#include <QWidget>
|
|
|
|
#include "ControlAction.h"
|
|
#include "FloatingPanel.h"
|
|
|
|
class GameWorldView;
|
|
class QLabel;
|
|
class QScrollArea;
|
|
class QTimer;
|
|
class QVBoxLayout;
|
|
|
|
// Shows the controls available in the player's current situation
|
|
// (REQ-UI-CONTROLS-PANEL). The panel decides nothing: which rows apply is
|
|
// ControlAction.h's answer, the same one the key handling and the world view's mouse
|
|
// dispatch act on, so what is shown and what happens cannot part company
|
|
// (REQ-UI-CONTROLS-ACCURACY).
|
|
//
|
|
// It floats over the game world at the left edge, bottom-aligned within the band its
|
|
// owner hands it, and collapses to its heading when the heading is clicked.
|
|
//
|
|
// Refreshed on a timer rather than by subscribing to events: two of the things that
|
|
// change a row -- a belt drag starting, the ghost moving over a transfer target --
|
|
// happen on mouse movement and publish nothing, and they still have to be reflected
|
|
// while the game is paused, so there is no tick to hang it on either. The rebuild is
|
|
// skipped unless the resolved content actually differs, which is a vector of enums to
|
|
// compare.
|
|
class ControlsPanel : public QWidget, public FloatingPanel
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
// Neither pointer is owned; both must outlive this widget. The view is the source
|
|
// of the control context, being the widget that owns the build mode and the
|
|
// selection.
|
|
ControlsPanel(const GameWorldView* view, QWidget* parent = nullptr);
|
|
|
|
// Places the panel in the bottom-left corner of the game world view. It shares the
|
|
// bottom edge with the build button bar rather than clearing its strip, the bar
|
|
// being centered and sized to its buttons and so usually leaving the left free; it
|
|
// rises above the bar only when the two would otherwise overlap
|
|
// (REQ-UI-CONTROLS-PANEL, REQ-UI-BUILD-BAR). The bar is the only thing placed before
|
|
// it, so it is the only rect it ever has to rise above.
|
|
void placeIn(const QRect& viewRect,
|
|
const std::vector<QRect>& occupiedRects) override;
|
|
|
|
protected:
|
|
// Clicking the heading collapses and expands the panel (REQ-UI-CONTROLS-PANEL).
|
|
void mousePressEvent(QMouseEvent* event) override;
|
|
|
|
private:
|
|
// Re-resolves the context and rebuilds only if the rows or the heading changed.
|
|
void refresh();
|
|
// Replaces the rows with those of the current context.
|
|
void rebuild(const ControlContext& context);
|
|
// The heading's "<name> * <detail>" text for the context, detail omitted when the
|
|
// context has none.
|
|
QString getHeadingText(const ControlContext& context) const;
|
|
// Asks for the placement pass to be re-run, the panel's own size having changed.
|
|
void invalidateLayout();
|
|
|
|
const GameWorldView* m_view;
|
|
|
|
QLabel* m_heading;
|
|
// Scrolls the rows once they outgrow the space the panel has (REQ-UI-CONTROLS-PANEL).
|
|
// The heading is deliberately outside it, so it stays put and stays clickable.
|
|
QScrollArea* m_scrollArea;
|
|
QWidget* m_rows;
|
|
QVBoxLayout* m_rowsLayout;
|
|
QTimer* m_refreshTimer;
|
|
|
|
// What is currently drawn, so a refresh that resolves to the same thing does
|
|
// nothing. The always-available block is kept separately because the divider
|
|
// between the two is part of what is drawn.
|
|
QString m_shownHeading;
|
|
std::vector<ControlAction> m_shownContextActions;
|
|
std::vector<ControlAction> m_shownAlwaysActions;
|
|
|
|
// Survives context changes and simulation restarts; presentation only, never a
|
|
// command (REQ-UI-CONTROLS-PANEL).
|
|
bool m_collapsed = false;
|
|
};
|