#pragma once #include #include #include #include #include #include "BuildingType.h" class BuildingIconCache; class ItemIconCache; class QHBoxLayout; class QVBoxLayout; // One recipe drawn: what it consumes, an arrow, what it produces, and how long a cycle // takes (REQ-UI-RECIPE-SUMMARY), under a header naming the building that runs it and the // recipe itself where the recipe has to identify itself. Every place the UI states what // something makes draws this same line, so a recipe reads alike wherever it is shown: // // * beneath the selection button, for the recipe a building is running // (REQ-UI-RECIPE-SUMMARY); // * on a selection dialog's option buttons, under the option's name // (REQ-UI-SELECT-OPTIONS); // * in an item's production tooltip and in the unlock-choice dialog, there led by the // icon of the building that runs the recipe and its name // (REQ-UI-ITEM-TOOLTIP, REQ-DEF-SCHEMATIC-DROP); // * on a module button and in the layout dialog's build cost, where there is a price // and a time but nothing produced (REQ-MOD-UI-DIALOG, REQ-MOD-UI-STATS-PANEL). class RecipeLineRow : public QWidget { Q_OBJECT public: // One item and how many of it the line states. struct Amount { std::string itemId; int amount = 0; bool operator==(const Amount& other) const { return itemId == other.itemId && amount == other.amount; } }; // What the line says. Everything but the inputs is optional, which is what lets the // one widget serve the summary, the option buttons, the tooltip lines and the cost // lines. struct Spec { // The chip of the building running the recipe, on the header line, where the // recipe has to say which building that is -- an item may have several producers // (REQ-UI-ITEM-TOOLTIP). Unset wherever the surrounding widget already // establishes the building, and the header line is then left out entirely. std::optional building; // The recipe's name, beside the building chip on the header line. Empty where the // name is the caption of the widget around this one instead. QString name; std::vector inputs; // Empty for a line that produces no item of its own: a ship schematic, or a // module's price. No arrow is drawn then. std::vector outputs; std::optional durationSeconds; // True where the time is added to something else rather than being a cycle of // its own, and so reads "+3.0 s" (REQ-MOD-UI-DIALOG). bool durationIsAddition = false; bool operator==(const Spec& other) const { return building == other.building && name == other.name && inputs == other.inputs && outputs == other.outputs && durationSeconds == other.durationSeconds && durationIsAddition == other.durationIsAddition; } bool isEmpty() const { return inputs.empty() && outputs.empty(); } }; // Both caches may be null, which leaves the icons off: an item with no square and // no icon falls back to its id, and a building with no chip to its name alone. // Neither is owned. RecipeLineRow(ItemIconCache* itemIcons, BuildingIconCache* buildingIcons, QWidget* parent = nullptr); // Hides the row when the spec has nothing to state, which is how a building with no // recipe shows no summary at all (REQ-UI-RECIPE-SUMMARY). void setLine(const Spec& spec); // Boxes the line in a card of its own, for the two places that stack several of them // (REQ-UI-ITEM-TOOLTIP, REQ-DEF-SCHEMATIC-DROP): a run of bare lines reads as one // field of icons and numbers, and the box is what separates one recipe from the next. // Off by default -- a line drawn inside a button or a panel section is already framed // by what holds it. void setCardChrome(bool enabled); private: void rebuild(const Spec& spec); void addAmounts(const std::vector& amounts); ItemIconCache* m_itemIcons; BuildingIconCache* m_buildingIcons; QVBoxLayout* m_outerLayout; // The two lines, each a row widget of its own so a rebuild only has to empty their // layouts -- no nested layout to take apart. The header is hidden where the spec // names neither a building nor a recipe. QWidget* m_headerRow; QHBoxLayout* m_headerLayout; QWidget* m_amountsRow; QHBoxLayout* m_amountsLayout; // What the row currently shows, so a refresh at tick rate rebuilds it only when the // recipe actually changed. Spec m_spec; };