show the available controls in a panel over the world
The panel decides nothing about what the controls are: it asks the same resolver the key handling and the mouse dispatch ask, and renders each badge from the binding that resolver matches. A chip cannot claim a key that does nothing, and a label cannot describe a click that does something else, because neither is written here. Display text is the one part that is not shared -- lib/core says what is available and what triggers it, ControlActionText.cpp says what it is called. That split is why the table can stay free of UI strings and still be the single source of the pairing. It refreshes on a timer rather than by subscribing. Two things that change a row -- a belt drag starting, the ghost crossing a transfer target -- happen on mouse movement and publish nothing, and they must still show while the game is paused, so there is no event and no tick to hang it on. Resolving is comparing two vectors of enums, and the rebuild is skipped unless they differ. Left edge, bottom-aligned in the same band the selection panel is confined to, so it clears the build bar's strip and never meets the panel on the opposite edge. Clicking the heading collapses it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
@@ -14,6 +14,8 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/HeaderBar.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildButtonBar.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SelectionPanel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ControlsPanel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ControlActionText.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintLibrary.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSelectionDialog.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutDialog.h
|
||||
@@ -45,6 +47,8 @@ SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/HeaderBar.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildButtonBar.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SelectionPanel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ControlsPanel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ControlActionText.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintLibrary.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSelectionDialog.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutDialog.cpp
|
||||
|
||||
92
src/ui/ControlActionText.cpp
Normal file
92
src/ui/ControlActionText.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "ControlActionText.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QKeySequence>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// tr() for a file of free functions. Q_DECLARE_TR_FUNCTIONS expands with access
|
||||
// specifiers, so it needs a class rather than a namespace; this struct exists only to
|
||||
// carry it and give the strings a single lupdate context.
|
||||
struct Strings
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(ControlActionText)
|
||||
};
|
||||
|
||||
QString getMouseBadge(MouseBinding binding)
|
||||
{
|
||||
switch (binding)
|
||||
{
|
||||
case MouseBinding::LeftClick: return Strings::tr("LMB");
|
||||
case MouseBinding::LeftDrag: return Strings::tr("LMB drag");
|
||||
case MouseBinding::CtrlLeftClick: return Strings::tr("Ctrl+LMB");
|
||||
case MouseBinding::CtrlLeftDrag: return Strings::tr("Ctrl+LMB drag");
|
||||
case MouseBinding::RightClick: return Strings::tr("RMB");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString getControlBindingBadge(const ControlBinding& binding)
|
||||
{
|
||||
if (binding.isMouse) { return getMouseBadge(binding.mouse); }
|
||||
|
||||
// QKeySequence spells the modifiers and the key together and localizes them, which
|
||||
// is what makes this stay correct once keys are rebindable: the chip is generated
|
||||
// from the binding rather than typed next to it.
|
||||
return QKeySequence(binding.key | static_cast<int>(binding.modifiers))
|
||||
.toString(QKeySequence::NativeText);
|
||||
}
|
||||
|
||||
QString getControlActionLabel(ControlAction action, const ControlContext& context)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ControlAction::None: return QString();
|
||||
case ControlAction::Move: return Strings::tr("Move");
|
||||
case ControlAction::GameSpeed: return Strings::tr("Game speed");
|
||||
case ControlAction::TogglePause: return Strings::tr("Toggle pause");
|
||||
case ControlAction::PasteTemporary: return Strings::tr("Paste last");
|
||||
case ControlAction::OpenBlueprints: return Strings::tr("Blueprints");
|
||||
case ControlAction::OpenMenu: return Strings::tr("Menu");
|
||||
// A click on empty space clears and a click on an object replaces; naming only the
|
||||
// first would be a half-truth once something is selected.
|
||||
case ControlAction::Select:
|
||||
return context.selection == ControlSelection::None
|
||||
? Strings::tr("Select")
|
||||
: Strings::tr("Select / clear selection");
|
||||
case ControlAction::SelectArea: return Strings::tr("Select area");
|
||||
case ControlAction::AddToSelection: return Strings::tr("Add / remove from selection");
|
||||
case ControlAction::AddAreaToSelection: return Strings::tr("Add area to selection");
|
||||
case ControlAction::EnterDeconstruct: return Strings::tr("Deconstruct mode");
|
||||
case ControlAction::CopyTemporary: return Strings::tr("Copy to temporary blueprint");
|
||||
case ControlAction::CreateBlueprint: return Strings::tr("Create blueprint");
|
||||
case ControlAction::Place: return Strings::tr("Place");
|
||||
case ControlAction::ApplySettings: return Strings::tr("Apply settings");
|
||||
case ControlAction::PlaceBeltLine: return Strings::tr("Place belt line");
|
||||
case ControlAction::Rotate: return Strings::tr("Rotate");
|
||||
case ControlAction::CancelBeltLine: return Strings::tr("Cancel belt line");
|
||||
case ControlAction::ExitMode:
|
||||
return context.mode == BuildMode::Deconstruct
|
||||
? Strings::tr("Exit deconstruct mode")
|
||||
: Strings::tr("Exit placement");
|
||||
case ControlAction::ToggleDeconstruct: return Strings::tr("Toggle deconstruct");
|
||||
case ControlAction::DeconstructArea: return Strings::tr("Deconstruct area");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString getControlContextName(ControlContextKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ControlContextKind::General: return Strings::tr("GENERAL");
|
||||
case ControlContextKind::Selection: return Strings::tr("SELECTION");
|
||||
case ControlContextKind::Build: return Strings::tr("BUILD MODE");
|
||||
case ControlContextKind::Blueprint: return Strings::tr("BLUEPRINT MODE");
|
||||
case ControlContextKind::Deconstruct: return Strings::tr("DECONSTRUCT MODE");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
26
src/ui/ControlActionText.h
Normal file
26
src/ui/ControlActionText.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "ControlAction.h"
|
||||
|
||||
// What the player is shown for the actions and bindings declared in ControlAction.h
|
||||
// (REQ-UI-CONTROLS-CONTENT). Kept out of lib/core deliberately: that file decides what
|
||||
// is available and what triggers it, this one decides what it is called, and only this
|
||||
// half is presentation.
|
||||
//
|
||||
// A badge is rendered from the binding it belongs to rather than written beside it, so
|
||||
// what the panel shows on a chip is what the resolver actually matches. When bindings
|
||||
// become player-configurable, this is the only place that has to learn to spell a
|
||||
// rebound key.
|
||||
|
||||
// The chip text for one binding: "Ctrl+V", "LMB drag", "RMB".
|
||||
QString getControlBindingBadge(const ControlBinding& binding);
|
||||
|
||||
// What the action is called in this context. A few actions are named for their
|
||||
// situation rather than their implementation -- one exit action reads "Exit placement"
|
||||
// in a placement mode and "Exit deconstruct mode" in deconstruct mode.
|
||||
QString getControlActionLabel(ControlAction action, const ControlContext& context);
|
||||
|
||||
// The heading, in the upper case the card shows it in (REQ-UI-CONTROLS-CARD).
|
||||
QString getControlContextName(ControlContextKind kind);
|
||||
239
src/ui/ControlsPanel.cpp
Normal file
239
src/ui/ControlsPanel.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include "ControlsPanel.h"
|
||||
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ControlActionText.h"
|
||||
#include "GameWorldView.h"
|
||||
#include "selection/SelectionNames.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const int kMarginPx = 8; // between the band's edge and the panel
|
||||
const int kCardMarginPx = 8; // inside the panel, around its content
|
||||
const int kRefreshMs = 50; // see the class comment on why this polls
|
||||
|
||||
// Separates the heading's name from its detail, e.g. "BUILD MODE * Assembler".
|
||||
const QChar kHeadingSeparator(0x00B7); // U+00B7 MIDDLE DOT
|
||||
|
||||
// One row: the chips for the bindings, then what the action is called. Built as a plain
|
||||
// widget rather than a class of its own -- it holds no state and answers no questions.
|
||||
QWidget* makeRow(ControlAction action, const ControlContext& context, QWidget* parent)
|
||||
{
|
||||
QWidget* row = new QWidget(parent);
|
||||
QHBoxLayout* layout = new QHBoxLayout(row);
|
||||
layout->setContentsMargins(0, 2, 0, 2);
|
||||
layout->setSpacing(4);
|
||||
|
||||
// Every badge is rendered from the binding the resolver matches, so a chip cannot
|
||||
// claim a key that does nothing (REQ-UI-CONTROLS-ACCURACY).
|
||||
const std::vector<ControlBinding> bindings = getControlActionBindings(action, context);
|
||||
for (const ControlBinding& binding : bindings)
|
||||
{
|
||||
QLabel* badge = new QLabel(getControlBindingBadge(binding), row);
|
||||
badge->setObjectName(QStringLiteral("controlBadge"));
|
||||
layout->addWidget(badge);
|
||||
}
|
||||
|
||||
QLabel* label = new QLabel(getControlActionLabel(action, context), row);
|
||||
label->setObjectName(action == ControlAction::ExitMode
|
||||
? QStringLiteral("controlLabelExit")
|
||||
: QStringLiteral("controlLabel"));
|
||||
layout->addSpacing(4);
|
||||
layout->addWidget(label);
|
||||
layout->addStretch(1);
|
||||
return row;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ControlsPanel::ControlsPanel(const GameWorldView* view, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_view(view)
|
||||
{
|
||||
// Floats over the rendered world, so it brings its own background to stay legible
|
||||
// over any world content (REQ-UI-CONTROLS-PANEL). Palette colors match the build
|
||||
// button bar's and the selection panel's chrome; like them this is widget chrome
|
||||
// rather than world rendering, so it is deliberately not a visuals.toml color. The
|
||||
// class scoped selector keeps the border on the panel rather than cascading onto
|
||||
// its children.
|
||||
setAttribute(Qt::WA_StyledBackground, true);
|
||||
setStyleSheet(QStringLiteral(
|
||||
"ControlsPanel { background-color: palette(window);"
|
||||
" border: 1px solid palette(mid); border-radius: 4px; }"
|
||||
"QLabel#controlHeading { font-weight: bold; letter-spacing: 1px;"
|
||||
" color: palette(text); }"
|
||||
"QLabel#controlBadge { border: 1px solid palette(mid); border-radius: 3px;"
|
||||
" padding: 1px 5px; font-family: monospace; color: palette(text); }"
|
||||
"QLabel#controlLabel { color: palette(text); }"
|
||||
// The row that leaves the mode reads differently from the ones that act within
|
||||
// it (REQ-UI-CONTROLS-CARD).
|
||||
"QLabel#controlLabelExit { color: palette(bright-text); }"
|
||||
"QLabel#controlCaption { color: palette(mid); font-size: 10px;"
|
||||
" letter-spacing: 1px; }"));
|
||||
|
||||
QVBoxLayout* outerLayout = new QVBoxLayout(this);
|
||||
outerLayout->setContentsMargins(kCardMarginPx, kCardMarginPx,
|
||||
kCardMarginPx, kCardMarginPx);
|
||||
outerLayout->setSpacing(4);
|
||||
|
||||
m_heading = new QLabel(this);
|
||||
m_heading->setObjectName(QStringLiteral("controlHeading"));
|
||||
m_heading->setCursor(Qt::PointingHandCursor);
|
||||
outerLayout->addWidget(m_heading);
|
||||
|
||||
m_rows = new QWidget(this);
|
||||
m_rowsLayout = new QVBoxLayout(m_rows);
|
||||
m_rowsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_rowsLayout->setSpacing(0);
|
||||
outerLayout->addWidget(m_rows);
|
||||
|
||||
// Polling rather than subscribing; see the class comment.
|
||||
m_refreshTimer = new QTimer(this);
|
||||
connect(m_refreshTimer, &QTimer::timeout, this, &ControlsPanel::refresh);
|
||||
m_refreshTimer->start(kRefreshMs);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void ControlsPanel::anchorTo(const QRect& bandRect)
|
||||
{
|
||||
m_bandRect = bandRect;
|
||||
refit();
|
||||
}
|
||||
|
||||
void ControlsPanel::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
// Only the heading toggles; a click anywhere else is swallowed so it never reaches
|
||||
// the world beneath (REQ-UI-CONTROLS-PANEL).
|
||||
if (m_heading->geometry().contains(event->pos()))
|
||||
{
|
||||
m_collapsed = !m_collapsed;
|
||||
m_rows->setVisible(!m_collapsed);
|
||||
refit();
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void ControlsPanel::refresh()
|
||||
{
|
||||
if (!m_view) { return; }
|
||||
|
||||
const ControlContext context = m_view->getControlContext();
|
||||
|
||||
const QString heading = getHeadingText(context);
|
||||
const std::vector<ControlAction> contextActions = getContextActions(context);
|
||||
const std::vector<ControlAction> alwaysActions = getAlwaysAvailableActions(context);
|
||||
|
||||
if (heading == m_shownHeading && contextActions == m_shownContextActions
|
||||
&& alwaysActions == m_shownAlwaysActions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_shownHeading = heading;
|
||||
m_shownContextActions = contextActions;
|
||||
m_shownAlwaysActions = alwaysActions;
|
||||
rebuild(context);
|
||||
}
|
||||
|
||||
void ControlsPanel::rebuild(const ControlContext& context)
|
||||
{
|
||||
m_heading->setText(m_shownHeading);
|
||||
|
||||
// Rows are rebuilt wholesale rather than reconciled: a context change replaces
|
||||
// nearly all of them, and the panel redraws only when something actually changed.
|
||||
while (QLayoutItem* item = m_rowsLayout->takeAt(0))
|
||||
{
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
|
||||
for (ControlAction action : m_shownContextActions)
|
||||
{
|
||||
m_rowsLayout->addWidget(makeRow(action, context, m_rows));
|
||||
}
|
||||
|
||||
// The always-available block sits under a divider, except in the General context,
|
||||
// where those rows and the context's own are the same kind of thing to a player
|
||||
// with nothing selected and no mode active (REQ-UI-CONTROLS-CARD).
|
||||
if (!m_shownAlwaysActions.empty()
|
||||
&& getControlContextKind(context) != ControlContextKind::General)
|
||||
{
|
||||
QFrame* divider = new QFrame(m_rows);
|
||||
divider->setFrameShape(QFrame::HLine);
|
||||
divider->setFrameShadow(QFrame::Plain);
|
||||
m_rowsLayout->addSpacing(6);
|
||||
m_rowsLayout->addWidget(divider);
|
||||
|
||||
QLabel* caption = new QLabel(tr("ALWAYS AVAILABLE"), m_rows);
|
||||
caption->setObjectName(QStringLiteral("controlCaption"));
|
||||
m_rowsLayout->addWidget(caption);
|
||||
}
|
||||
|
||||
for (ControlAction action : m_shownAlwaysActions)
|
||||
{
|
||||
m_rowsLayout->addWidget(makeRow(action, context, m_rows));
|
||||
}
|
||||
|
||||
m_rows->setVisible(!m_collapsed);
|
||||
refit();
|
||||
}
|
||||
|
||||
QString ControlsPanel::getHeadingText(const ControlContext& context) const
|
||||
{
|
||||
const ControlContextKind kind = getControlContextKind(context);
|
||||
const QString name = getControlContextName(kind);
|
||||
|
||||
QString detail;
|
||||
switch (kind)
|
||||
{
|
||||
case ControlContextKind::Build:
|
||||
detail = getBuildingTypeName(context.builderType);
|
||||
break;
|
||||
case ControlContextKind::Blueprint:
|
||||
{
|
||||
// The temporary blueprint is never named (REQ-UI-BLUEPRINT-TEMP), so it is
|
||||
// labelled by what it is rather than left blank.
|
||||
const QString blueprintName = m_view->getActiveBlueprintName();
|
||||
detail = blueprintName.isEmpty() ? tr("Temporary") : blueprintName;
|
||||
break;
|
||||
}
|
||||
case ControlContextKind::Selection:
|
||||
detail = context.selection == ControlSelection::Buildings
|
||||
? tr("%n building(s)", "", context.selectionCount)
|
||||
: tr("%n object(s)", "", context.selectionCount);
|
||||
break;
|
||||
case ControlContextKind::General:
|
||||
case ControlContextKind::Deconstruct:
|
||||
break;
|
||||
}
|
||||
|
||||
if (detail.isEmpty()) { return name; }
|
||||
return name + QStringLiteral(" ") + kHeadingSeparator + QStringLiteral(" ") + detail;
|
||||
}
|
||||
|
||||
void ControlsPanel::refit()
|
||||
{
|
||||
if (m_bandRect.isNull()) { return; }
|
||||
|
||||
// The layout drops hidden widgets from its size hint, but only once it has been
|
||||
// re-run: collapsing hides the rows before Qt would get around to it on its own.
|
||||
layout()->activate();
|
||||
|
||||
const QRect band = m_bandRect.adjusted(kMarginPx, kMarginPx, -kMarginPx, -kMarginPx);
|
||||
if (band.width() <= 0 || band.height() <= 0) { return; }
|
||||
|
||||
const QSize hint = sizeHint();
|
||||
const int widthPx = qMin(hint.width(), band.width());
|
||||
const int heightPx = qMin(hint.height(), band.height());
|
||||
|
||||
// Left edge of the band, sitting on its bottom edge, so the panel grows upward as
|
||||
// rows are added (REQ-UI-CONTROLS-PANEL).
|
||||
setGeometry(band.left(), band.bottom() - heightPx + 1, widthPx, heightPx);
|
||||
}
|
||||
83
src/ui/ControlsPanel.h
Normal file
83
src/ui/ControlsPanel.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#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;
|
||||
};
|
||||
@@ -1156,6 +1156,12 @@ ControlContext GameWorldView::getControlContext() const
|
||||
return context;
|
||||
}
|
||||
|
||||
QString GameWorldView::getActiveBlueprintName() const
|
||||
{
|
||||
if (!m_buildMode.isBlueprintMode()) { return QString(); }
|
||||
return m_buildMode.getBlueprint().name;
|
||||
}
|
||||
|
||||
void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
const WorldCoordinates coordinates = getCoordinates();
|
||||
|
||||
@@ -114,6 +114,10 @@ public:
|
||||
// build mode and the selection.
|
||||
ControlContext getControlContext() const;
|
||||
|
||||
// Name of the blueprint being placed, for the controls panel's heading. Empty while
|
||||
// no blueprint is active and for the unnamed temporary one (REQ-UI-BLUEPRINT-TEMP).
|
||||
QString getActiveBlueprintName() const;
|
||||
|
||||
protected:
|
||||
void initializeGL() override;
|
||||
void paintGL() override;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "SchematicChoiceDialog.h"
|
||||
#include "HeaderBar.h"
|
||||
#include "SelectionPanel.h"
|
||||
#include "ControlsPanel.h"
|
||||
#include "ShipLayoutBlueprintSerializer.h"
|
||||
#include "ShipLayoutDialog.h"
|
||||
#include "BuildingIconCache.h"
|
||||
@@ -91,6 +92,11 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
|
||||
m_itemIcons.get(), m_buildingIcons.get(),
|
||||
this);
|
||||
|
||||
// Floats at the world view's opposite edge from the selection panel and reads the
|
||||
// world view for the player's current situation (REQ-UI-CONTROLS-PANEL). Built
|
||||
// after the view for the same stacking reason as the panels above it.
|
||||
m_controlsPanel = new ControlsPanel(m_gameWorldView, this);
|
||||
|
||||
// Created last so it stacks above the other children; covers the whole window and
|
||||
// dims the game behind modal dialogs/menus (REQ-UI-MODAL-DIM).
|
||||
m_dimOverlay = new ModalDimOverlay(m_visuals.overlays.modalDim, this);
|
||||
@@ -173,6 +179,10 @@ void MainWindow::layoutPanels()
|
||||
// move for it (REQ-UI-SELECTION-PANEL, REQ-UI-BUILD-BAR).
|
||||
m_selectionPanel->anchorTo(
|
||||
worldRect.adjusted(0, 0, 0, -m_buildButtonBar->getStripHeightPx()));
|
||||
// Same band, opposite edge: left and bottom-aligned, so it clears the bar's strip
|
||||
// too and never meets the selection panel (REQ-UI-CONTROLS-PANEL).
|
||||
m_controlsPanel->anchorTo(
|
||||
worldRect.adjusted(0, 0, 0, -m_buildButtonBar->getStripHeightPx()));
|
||||
m_dimOverlay->setGeometry(0, 0, totalW, totalH);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ class Simulation;
|
||||
class GameWorldView;
|
||||
class HeaderBar;
|
||||
class SelectionPanel;
|
||||
class ControlsPanel;
|
||||
class BuildButtonBar;
|
||||
class BlueprintLibrary;
|
||||
class BuildingIconCache;
|
||||
@@ -99,6 +100,7 @@ private:
|
||||
GameWorldView* m_gameWorldView;
|
||||
HeaderBar* m_headerBar;
|
||||
SelectionPanel* m_selectionPanel;
|
||||
ControlsPanel* m_controlsPanel;
|
||||
BuildButtonBar* m_buildButtonBar;
|
||||
// The saved blueprints themselves; they have no widget of their own any more and
|
||||
// are reached through the two modal dialogs (REQ-UI-BLUEPRINT-DIALOG).
|
||||
|
||||
Reference in New Issue
Block a user