Files
dota_factory/src/ui/ControlsPanel.h

84 lines
3.2 KiB
C++

#pragma once
#include <vector>
#include <QRect>
#include <QString>
#include <QWidget>
#include "ControlAction.h"
class GameWorldView;
class QLabel;
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
{
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);
// Confines the panel to the given band of the game world view: it left-aligns
// within it and sits on its bottom edge. The band is the world view less the strip
// the build button bar occupies, so the two never overlap and the bar never has to
// move (REQ-UI-CONTROLS-PANEL, REQ-UI-BUILD-BAR).
void anchorTo(const QRect& bandRect);
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;
// Re-fits the panel to its content within the anchored band.
void refit();
const GameWorldView* m_view;
QLabel* m_heading;
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;
// The band the panel confines itself to, in the coordinates of its parent; null
// until the owner has anchored it for the first time.
QRect m_bandRect;
};