mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-09 23:50:25 +00:00
Change: Unify station/waypoint/roadstop/object build-picker window code.
These windows now share a common code base for choosing and display class and types. An additional text filter is added to search types by name instead of just classes.
This commit is contained in:
parent
d2c8b476b5
commit
cdc356e7bf
@ -338,6 +338,8 @@ add_files(
|
||||
palette_func.h
|
||||
pbs.cpp
|
||||
pbs.h
|
||||
picker_gui.cpp
|
||||
picker_gui.h
|
||||
progress.cpp
|
||||
progress.h
|
||||
querystring_gui.h
|
||||
|
@ -2785,7 +2785,6 @@ STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP :{BLACK}Select r
|
||||
|
||||
# Rail waypoint construction window
|
||||
STR_WAYPOINT_CAPTION :{WHITE}Waypoint
|
||||
STR_WAYPOINT_GRAPHICS_TOOLTIP :{BLACK}Select waypoint type
|
||||
|
||||
# Rail station construction window
|
||||
STR_STATION_BUILD_RAIL_CAPTION :{WHITE}Rail Station Selection
|
||||
@ -2798,8 +2797,16 @@ STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP :{BLACK}Select l
|
||||
STR_STATION_BUILD_DRAG_DROP :{BLACK}Drag & Drop
|
||||
STR_STATION_BUILD_DRAG_DROP_TOOLTIP :{BLACK}Build a station using drag & drop
|
||||
|
||||
STR_STATION_BUILD_STATION_CLASS_TOOLTIP :{BLACK}Select a station class to display
|
||||
STR_STATION_BUILD_STATION_TYPE_TOOLTIP :{BLACK}Select the station type to build
|
||||
STR_PICKER_STATION_CLASS_TOOLTIP :Select a station class to display
|
||||
STR_PICKER_STATION_TYPE_TOOLTIP :Select a station type to build. Ctrl+Click to add or remove in saved items
|
||||
STR_PICKER_WAYPOINT_CLASS_TOOLTIP :Select a waypoint class to display
|
||||
STR_PICKER_WAYPOINT_TYPE_TOOLTIP :Select a waypoint to build. Ctrl+Click to add or remove in saved items
|
||||
STR_PICKER_ROADSTOP_BUS_CLASS_TOOLTIP :Select a bus station class to display
|
||||
STR_PICKER_ROADSTOP_BUS_TYPE_TOOLTIP :Select a bus station type to build. Ctrl+Click to add or remove in saved items
|
||||
STR_PICKER_ROADSTOP_TRUCK_CLASS_TOOLTIP :Select a lorry station class to display
|
||||
STR_PICKER_ROADSTOP_TRUCK_TYPE_TOOLTIP :Select a lorry station type to build. Ctrl+Click to add or remove in saved items
|
||||
STR_PICKER_OBJECT_CLASS_TOOLTIP :Select an object class to display
|
||||
STR_PICKER_OBJECT_TYPE_TOOLTIP :Select an object type to build. Ctrl+Click to add or remove in saved items. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only
|
||||
|
||||
STR_STATION_CLASS_DFLT :Default
|
||||
STR_STATION_CLASS_DFLT_STATION :Default station
|
||||
@ -2942,8 +2949,6 @@ STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Purchase
|
||||
|
||||
# Object construction window
|
||||
STR_OBJECT_BUILD_CAPTION :{WHITE}Object Selection
|
||||
STR_OBJECT_BUILD_TOOLTIP :{BLACK}Select object to build. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only
|
||||
STR_OBJECT_BUILD_CLASS_TOOLTIP :{BLACK}Select class of the object to build
|
||||
STR_OBJECT_BUILD_PREVIEW_TOOLTIP :{BLACK}Preview of the object
|
||||
STR_OBJECT_BUILD_SIZE :{BLACK}Size: {GOLD}{NUM} x {NUM} tiles
|
||||
|
||||
|
@ -42,6 +42,9 @@ private:
|
||||
static void InsertDefaults();
|
||||
|
||||
public:
|
||||
using spec_type = Tspec;
|
||||
using index_type = Tindex;
|
||||
|
||||
uint32_t global_id; ///< Global ID for class, e.g. 'DFLT', 'WAYP', etc.
|
||||
StringID name; ///< Name of this class.
|
||||
|
||||
@ -67,8 +70,6 @@ public:
|
||||
uint GetSpecCount() const { return static_cast<uint>(this->spec.size()); }
|
||||
/** Get the number of potentially user-available specs within the class. */
|
||||
uint GetUISpecCount() const { return this->ui_count; }
|
||||
int GetUIFromIndex(int index) const;
|
||||
int GetIndexFromUI(int ui_index) const;
|
||||
|
||||
const Tspec *GetSpec(uint index) const;
|
||||
|
||||
@ -80,7 +81,6 @@ public:
|
||||
static void Assign(Tspec *spec);
|
||||
static uint GetClassCount();
|
||||
static uint GetUIClassCount();
|
||||
static Tindex GetUIClass(uint index);
|
||||
static NewGRFClass *Get(Tindex class_index);
|
||||
|
||||
static const Tspec *GetByGrf(uint32_t grfid, uint16_t local_id);
|
||||
|
@ -105,21 +105,6 @@ uint NewGRFClass<Tspec, Tindex, Tmax>::GetUIClassCount()
|
||||
return std::count_if(std::begin(NewGRFClass::classes), std::end(NewGRFClass::classes), [](const auto &cls) { return cls.GetUISpecCount() > 0; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nth-class with user available specs.
|
||||
* @param index UI index of a class.
|
||||
* @return The class ID of the class.
|
||||
*/
|
||||
template <typename Tspec, typename Tindex, Tindex Tmax>
|
||||
Tindex NewGRFClass<Tspec, Tindex, Tmax>::GetUIClass(uint index)
|
||||
{
|
||||
for (const auto &cls : NewGRFClass::classes) {
|
||||
if (cls.GetUISpecCount() == 0) continue;
|
||||
if (index-- == 0) return cls.Index();
|
||||
}
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a spec from the class at a given index.
|
||||
* @param index The index where to find the spec.
|
||||
@ -132,38 +117,6 @@ const Tspec *NewGRFClass<Tspec, Tindex, Tmax>::GetSpec(uint index) const
|
||||
return index < this->GetSpecCount() ? this->spec[index] : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a UI spec index into a spec index.
|
||||
* @param ui_index UI index of the spec.
|
||||
* @return index of the spec, or -1 if out of range.
|
||||
*/
|
||||
template <typename Tspec, typename Tindex, Tindex Tmax>
|
||||
int NewGRFClass<Tspec, Tindex, Tmax>::GetIndexFromUI(int ui_index) const
|
||||
{
|
||||
if (ui_index < 0) return -1;
|
||||
for (uint i = 0; i < this->GetSpecCount(); i++) {
|
||||
if (!this->IsUIAvailable(i)) continue;
|
||||
if (ui_index-- == 0) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a spec index into a UI spec index.
|
||||
* @param index index of the spec.
|
||||
* @return UI index of the spec, or -1 if out of range.
|
||||
*/
|
||||
template <typename Tspec, typename Tindex, Tindex Tmax>
|
||||
int NewGRFClass<Tspec, Tindex, Tmax>::GetUIFromIndex(int index) const
|
||||
{
|
||||
if ((uint)index >= this->GetSpecCount()) return -1;
|
||||
uint ui_index = 0;
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (this->IsUIAvailable(i)) ui_index++;
|
||||
}
|
||||
return ui_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a spec by GRF location.
|
||||
* @param grfid GRF ID of spec.
|
||||
@ -177,7 +130,8 @@ const Tspec *NewGRFClass<Tspec, Tindex, Tmax>::GetByGrf(uint32_t grfid, uint16_t
|
||||
for (const auto &cls : NewGRFClass::classes) {
|
||||
for (const auto &spec : cls.spec) {
|
||||
if (spec == nullptr) continue;
|
||||
if (spec->grf_prop.grffile->grfid == grfid && spec->grf_prop.local_id == local_id) return spec;
|
||||
if (spec->grf_prop.local_id != local_id) continue;
|
||||
if ((spec->grf_prop.grffile == nullptr ? 0 : spec->grf_prop.grffile->grfid) == grfid) return spec;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,8 @@
|
||||
#include "newgrf_object.h"
|
||||
#include "newgrf_text.h"
|
||||
#include "object.h"
|
||||
#include "querystring_gui.h"
|
||||
#include "sortlist_type.h"
|
||||
#include "stringfilter_type.h"
|
||||
#include "string_func.h"
|
||||
#include "picker_gui.h"
|
||||
#include "sound_func.h"
|
||||
#include "strings_func.h"
|
||||
#include "viewport_func.h"
|
||||
#include "tilehighlight_func.h"
|
||||
@ -34,199 +32,108 @@
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
static ObjectClassID _selected_object_class; ///< Currently selected available object class.
|
||||
static int _selected_object_index; ///< Index of the currently selected object if existing, else \c -1.
|
||||
static uint8_t _selected_object_view; ///< the view of the selected object
|
||||
|
||||
/** Enum referring to the Hotkeys in the build object window */
|
||||
enum BuildObjectHotkeys {
|
||||
BOHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
|
||||
struct ObjectPickerSelection {
|
||||
ObjectClassID sel_class; ///< Selected object class.
|
||||
uint16_t sel_type; ///< Selected object type within the class.
|
||||
uint8_t sel_view; ///< Selected view of the object.
|
||||
};
|
||||
static ObjectPickerSelection _object_gui; ///< Settings of the object picker.
|
||||
|
||||
class ObjectPickerCallbacks : public PickerCallbacksNewGRFClass<ObjectClass> {
|
||||
public:
|
||||
StringID GetClassTooltip() const override { return STR_PICKER_OBJECT_CLASS_TOOLTIP; }
|
||||
StringID GetTypeTooltip() const override { return STR_PICKER_OBJECT_TYPE_TOOLTIP; }
|
||||
|
||||
bool IsActive() const override
|
||||
{
|
||||
for (const auto &cls : ObjectClass::Classes()) {
|
||||
for (const auto *spec : cls.Specs()) {
|
||||
if (spec != nullptr && spec->IsEverAvailable()) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int GetSelectedClass() const override { return _object_gui.sel_class; }
|
||||
void SetSelectedClass(int id) const override { _object_gui.sel_class = this->GetClassIndex(id); }
|
||||
|
||||
StringID GetClassName(int id) const override
|
||||
{
|
||||
const auto *objclass = this->GetClass(id);
|
||||
if (objclass->GetUISpecCount() == 0) return INVALID_STRING_ID;
|
||||
return objclass->name;
|
||||
}
|
||||
|
||||
int GetSelectedType() const override { return _object_gui.sel_type; }
|
||||
void SetSelectedType(int id) const override { _object_gui.sel_type = id; }
|
||||
|
||||
StringID GetTypeName(int cls_id, int id) const override
|
||||
{
|
||||
const auto *spec = this->GetSpec(cls_id, id);
|
||||
return (spec == nullptr || !spec->IsEverAvailable()) ? INVALID_STRING_ID : spec->name;
|
||||
}
|
||||
|
||||
bool IsTypeAvailable(int cls_id, int id) const override
|
||||
{
|
||||
const auto *spec = this->GetSpec(cls_id, id);
|
||||
return spec->IsAvailable();
|
||||
}
|
||||
|
||||
void DrawType(int x, int y, int cls_id, int id) const override
|
||||
{
|
||||
const auto *spec = this->GetSpec(cls_id, id);
|
||||
if (spec->grf_prop.grffile == nullptr) {
|
||||
extern const DrawTileSprites _objects[];
|
||||
const DrawTileSprites *dts = &_objects[spec->grf_prop.local_id];
|
||||
DrawOrigTileSeqInGUI(x, y, dts, PAL_NONE);
|
||||
} else {
|
||||
DrawNewObjectTileInGUI(x, y, spec, std::min<int>(_object_gui.sel_view, spec->views - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static ObjectPickerCallbacks instance;
|
||||
};
|
||||
/* static */ ObjectPickerCallbacks ObjectPickerCallbacks::instance;
|
||||
|
||||
/** The window used for building objects. */
|
||||
class BuildObjectWindow : public Window {
|
||||
typedef GUIList<ObjectClassID, std::nullptr_t, StringFilter &> GUIObjectClassList; ///< Type definition for the list to hold available object classes.
|
||||
|
||||
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.
|
||||
|
||||
int object_margin; ///< The margin (in pixels) around an object.
|
||||
int line_height; ///< The height of a single line.
|
||||
int info_height; ///< The height of the info box.
|
||||
Scrollbar *vscroll; ///< The scrollbar.
|
||||
|
||||
static Listing last_sorting; ///< Default sorting of #GUIObjectClassList.
|
||||
static Filtering last_filtering; ///< Default filtering of #GUIObjectClassList.
|
||||
static const std::initializer_list<GUIObjectClassList::SortFunction * const> sorter_funcs; ///< Sort functions of the #GUIObjectClassList.
|
||||
static const std::initializer_list<GUIObjectClassList::FilterFunction * const> filter_funcs; ///< Filter functions of the #GUIObjectClassList.
|
||||
GUIObjectClassList object_classes; ///< Available object classes.
|
||||
StringFilter string_filter; ///< Filter for available objects.
|
||||
QueryString filter_editbox; ///< Filter editbox.
|
||||
|
||||
/** Scroll #WID_BO_CLASS_LIST so that the selected object class is visible. */
|
||||
void EnsureSelectedObjectClassIsVisible()
|
||||
{
|
||||
uint pos = 0;
|
||||
for (auto object_class_id : this->object_classes) {
|
||||
if (object_class_id == _selected_object_class) break;
|
||||
pos++;
|
||||
}
|
||||
this->vscroll->ScrollTowards(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the previously selected object can be selected.
|
||||
* @return \c true if the selected object is available, \c false otherwise.
|
||||
*/
|
||||
bool CanRestoreSelectedObject()
|
||||
{
|
||||
if (_selected_object_index == -1) return false;
|
||||
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
if ((int)objclass->GetSpecCount() <= _selected_object_index) return false;
|
||||
|
||||
return objclass->GetSpec(_selected_object_index)->IsAvailable();
|
||||
}
|
||||
class BuildObjectWindow : public PickerWindow {
|
||||
int info_height; ///< The height of the info box.
|
||||
|
||||
public:
|
||||
BuildObjectWindow(WindowDesc *desc, WindowNumber number) : Window(desc), info_height(1), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
|
||||
BuildObjectWindow(WindowDesc *desc, WindowNumber) : PickerWindow(desc, nullptr, 0, ObjectPickerCallbacks::instance), info_height(1)
|
||||
{
|
||||
this->CreateNestedTree();
|
||||
|
||||
this->vscroll = this->GetScrollbar(WID_BO_SCROLLBAR);
|
||||
|
||||
this->querystrings[WID_BO_FILTER] = &this->filter_editbox;
|
||||
|
||||
this->object_classes.SetListing(this->last_sorting);
|
||||
this->object_classes.SetFiltering(this->last_filtering);
|
||||
this->object_classes.SetSortFuncs(this->sorter_funcs);
|
||||
this->object_classes.SetFilterFuncs(this->filter_funcs);
|
||||
this->object_classes.ForceRebuild();
|
||||
|
||||
BuildObjectClassesAvailable();
|
||||
SelectClassAndObject();
|
||||
|
||||
this->FinishInitNested(number);
|
||||
|
||||
NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BO_SELECT_MATRIX);
|
||||
matrix->SetScrollbar(this->GetScrollbar(WID_BO_SELECT_SCROLL));
|
||||
matrix->SetCount(ObjectClass::Get(_selected_object_class)->GetUISpecCount());
|
||||
|
||||
this->GetWidget<NWidgetMatrix>(WID_BO_OBJECT_MATRIX)->SetCount(4);
|
||||
|
||||
ResetObjectToPlace();
|
||||
|
||||
this->vscroll->SetCount(this->object_classes.size());
|
||||
|
||||
EnsureSelectedObjectClassIsVisible();
|
||||
|
||||
this->ConstructWindow();
|
||||
this->InvalidateData();
|
||||
}
|
||||
|
||||
/** Sort object classes by ObjectClassID. */
|
||||
static bool ObjectClassIDSorter(ObjectClassID const &a, ObjectClassID const &b)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
/** Filter object classes by class name. */
|
||||
static bool TagNameFilter(ObjectClassID const *oc, StringFilter &filter)
|
||||
{
|
||||
ObjectClass *objclass = ObjectClass::Get(*oc);
|
||||
|
||||
filter.ResetState();
|
||||
filter.AddLine(GetString(objclass->name));
|
||||
return filter.GetState();
|
||||
}
|
||||
|
||||
/** Builds the filter list of available object classes. */
|
||||
void BuildObjectClassesAvailable()
|
||||
{
|
||||
if (!this->object_classes.NeedRebuild()) return;
|
||||
|
||||
this->object_classes.clear();
|
||||
this->object_classes.reserve(ObjectClass::GetClassCount());
|
||||
|
||||
for (const auto &cls : ObjectClass::Classes()) {
|
||||
if (cls.GetUISpecCount() == 0) continue; // Is this needed here?
|
||||
object_classes.push_back(cls.Index());
|
||||
}
|
||||
|
||||
this->object_classes.Filter(this->string_filter);
|
||||
this->object_classes.RebuildDone();
|
||||
this->object_classes.Sort();
|
||||
|
||||
this->vscroll->SetCount(this->object_classes.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the previously selected current object class and object
|
||||
* can be shown as selected to the user when the dialog is opened.
|
||||
*/
|
||||
void SelectClassAndObject()
|
||||
{
|
||||
assert(!this->object_classes.empty()); // object GUI should be disabled elsewise
|
||||
if (_selected_object_class == ObjectClassID::OBJECT_CLASS_BEGIN) {
|
||||
/* This happens during the first time the window is open during the game life cycle. */
|
||||
this->SelectOtherClass(this->object_classes[0]);
|
||||
} else {
|
||||
/* Check if the previously selected object class is not available anymore as a
|
||||
* result of starting a new game without the corresponding NewGRF. */
|
||||
bool available = _selected_object_class < ObjectClass::GetClassCount();
|
||||
this->SelectOtherClass(available ? _selected_object_class : this->object_classes[0]);
|
||||
}
|
||||
|
||||
if (this->CanRestoreSelectedObject()) {
|
||||
this->SelectOtherObject(_selected_object_index);
|
||||
} else {
|
||||
this->SelectFirstAvailableObject(true);
|
||||
}
|
||||
assert(ObjectClass::Get(_selected_object_class)->GetUISpecCount() > 0); // object GUI should be disabled elsewise
|
||||
}
|
||||
|
||||
void SetStringParameters(WidgetID widget) const override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BO_OBJECT_NAME: {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
SetDParam(0, spec != nullptr ? spec->name : STR_EMPTY);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_OBJECT_SIZE: {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type);
|
||||
int size = spec == nullptr ? 0 : spec->size;
|
||||
SetDParam(0, GB(size, HasBit(_selected_object_view, 0) ? 4 : 0, 4));
|
||||
SetDParam(1, GB(size, HasBit(_selected_object_view, 0) ? 0 : 4, 4));
|
||||
SetDParam(0, GB(size, HasBit(_object_gui.sel_view, 0) ? 4 : 0, 4));
|
||||
SetDParam(1, GB(size, HasBit(_object_gui.sel_view, 0) ? 0 : 4, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
default:
|
||||
this->PickerWindow::SetStringParameters(widget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnInit() override
|
||||
{
|
||||
this->object_margin = ScaleGUITrad(4);
|
||||
this->GetWidget<NWidgetMatrix>(WID_BO_OBJECT_MATRIX)->SetCount(4);
|
||||
this->PickerWindow::OnInit();
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BO_CLASS_LIST: {
|
||||
for (auto object_class_id : this->object_classes) {
|
||||
ObjectClass *objclass = ObjectClass::Get(object_class_id);
|
||||
if (objclass->GetUISpecCount() == 0) continue;
|
||||
size.width = std::max(size.width, GetStringBoundingBox(objclass->name).width + padding.width);
|
||||
}
|
||||
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
|
||||
resize.height = this->line_height;
|
||||
size.height = 5 * this->line_height;
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_OBJECT_NAME:
|
||||
case WID_BO_OBJECT_SIZE:
|
||||
/* We do not want the window to resize when selecting objects; better clip texts */
|
||||
size.width = 0;
|
||||
@ -234,8 +141,8 @@ public:
|
||||
|
||||
case WID_BO_OBJECT_MATRIX: {
|
||||
/* Get the right amount of buttons based on the current spec. */
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type);
|
||||
if (spec != nullptr) {
|
||||
if (spec->views >= 2) size.width += resize.width;
|
||||
if (spec->views >= 4) size.height += resize.height;
|
||||
@ -246,41 +153,14 @@ public:
|
||||
}
|
||||
|
||||
case WID_BO_OBJECT_SPRITE: {
|
||||
bool two_wide = false; // Whether there will be two widgets next to each other in the matrix or not.
|
||||
uint height[2] = {0, 0}; // The height for the different views; in this case views 1/2 and 4.
|
||||
|
||||
/* Get the height and view information. */
|
||||
for (const auto &spec : ObjectSpec::Specs()) {
|
||||
if (!spec.IsEverAvailable()) continue;
|
||||
two_wide |= spec.views >= 2;
|
||||
height[spec.views / 4] = std::max<int>(spec.height, height[spec.views / 4]);
|
||||
}
|
||||
|
||||
/* Determine the pixel heights. */
|
||||
for (auto &h : height) {
|
||||
h *= ScaleGUITrad(TILE_HEIGHT);
|
||||
h += ScaleGUITrad(TILE_PIXELS) + 2 * this->object_margin;
|
||||
}
|
||||
|
||||
/* Now determine the size of the minimum widgets. When there are two columns, then
|
||||
* we want these columns to be slightly less wide. When there are two rows, then
|
||||
* determine the size of the widgets based on the maximum size for a single row
|
||||
* of widgets, or just the twice the widget height of the two row ones. */
|
||||
size.height = std::max(height[0], height[1] * 2);
|
||||
if (two_wide) {
|
||||
size.width = (3 * ScaleGUITrad(TILE_PIXELS) + 2 * this->object_margin) * 2;
|
||||
} else {
|
||||
size.width = 4 * ScaleGUITrad(TILE_PIXELS) + 2 * this->object_margin;
|
||||
}
|
||||
|
||||
/* Get the right size for the single widget based on the current spec. */
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
/* Get the right amount of buttons based on the current spec. */
|
||||
const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type);
|
||||
size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
if (spec != nullptr) {
|
||||
if (spec->views <= 1) size.width += WidgetDimensions::scaled.hsep_normal;
|
||||
if (spec->views <= 2) size.height += WidgetDimensions::scaled.vsep_normal;
|
||||
if (spec->views >= 2) size.width /= 2;
|
||||
if (spec->views >= 4) size.height /= 2;
|
||||
if (spec->views <= 1) size.width = size.width * 2 + WidgetDimensions::scaled.hsep_normal;
|
||||
if (spec->views <= 2) size.height = size.height * 2 + WidgetDimensions::scaled.vsep_normal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -289,103 +169,49 @@ public:
|
||||
size.height = this->info_height;
|
||||
break;
|
||||
|
||||
case WID_BO_SELECT_MATRIX:
|
||||
fill.height = 1;
|
||||
resize.height = 1;
|
||||
default:
|
||||
this->PickerWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
|
||||
break;
|
||||
|
||||
case WID_BO_SELECT_IMAGE:
|
||||
size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size.height = ScaleGUITrad(58) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget(const Rect &r, WidgetID widget) const override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BO_CLASS_LIST: {
|
||||
Rect mr = r.Shrink(WidgetDimensions::scaled.matrix);
|
||||
uint pos = 0;
|
||||
for (auto object_class_id : this->object_classes) {
|
||||
ObjectClass *objclass = ObjectClass::Get(object_class_id);
|
||||
if (objclass->GetUISpecCount() == 0) continue;
|
||||
if (!this->vscroll->IsVisible(pos++)) continue;
|
||||
DrawString(mr, objclass->name,
|
||||
(object_class_id == _selected_object_class) ? TC_WHITE : TC_BLACK);
|
||||
mr.top += this->line_height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_OBJECT_SPRITE: {
|
||||
if (_selected_object_index == -1) break;
|
||||
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type);
|
||||
if (spec == nullptr) break;
|
||||
|
||||
/* Height of the selection matrix.
|
||||
* Depending on the number of views, the matrix has a 1x1, 1x2, 2x1 or 2x2 layout. To make the previews
|
||||
* look nice in all layouts, we use the 4x4 layout (smallest previews) as starting point. For the bigger
|
||||
* previews in the layouts with less views we add space homogeneously on all sides, so the 4x4 preview-rectangle
|
||||
* is centered in the 2x1, 1x2 resp. 1x1 buttons. */
|
||||
const NWidgetMatrix *matrix = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>();
|
||||
uint matrix_height = matrix->current_y;
|
||||
|
||||
DrawPixelInfo tmp_dpi;
|
||||
/* Set up a clipping area for the preview. */
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
|
||||
if (FillDrawPixelInfo(&tmp_dpi, ir)) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT);
|
||||
int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM);
|
||||
|
||||
if (spec->grf_prop.grffile == nullptr) {
|
||||
extern const DrawTileSprites _objects[];
|
||||
const DrawTileSprites *dts = &_objects[spec->grf_prop.local_id];
|
||||
DrawOrigTileSeqInGUI(ir.Width() / 2 - 1, (ir.Height() + matrix_height / 2) / 2 - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), dts, PAL_NONE);
|
||||
DrawOrigTileSeqInGUI(x, y, dts, PAL_NONE);
|
||||
} else {
|
||||
DrawNewObjectTileInGUI(ir.Width() / 2 - 1, (ir.Height() + matrix_height / 2) / 2 - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), spec, matrix->GetCurrentElement());
|
||||
DrawNewObjectTileInGUI(x, y, spec, matrix->GetCurrentElement());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_SELECT_IMAGE: {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
int obj_index = objclass->GetIndexFromUI(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement());
|
||||
if (obj_index < 0) break;
|
||||
const ObjectSpec *spec = objclass->GetSpec(obj_index);
|
||||
if (spec == nullptr) break;
|
||||
|
||||
DrawPixelInfo tmp_dpi;
|
||||
/* Set up a clipping area for the preview. */
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
|
||||
if (FillDrawPixelInfo(&tmp_dpi, ir)) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
if (spec->grf_prop.grffile == nullptr) {
|
||||
extern const DrawTileSprites _objects[];
|
||||
const DrawTileSprites *dts = &_objects[spec->grf_prop.local_id];
|
||||
DrawOrigTileSeqInGUI(ir.Width() / 2 - 1, ir.Height() - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), dts, PAL_NONE);
|
||||
} else {
|
||||
DrawNewObjectTileInGUI(ir.Width() / 2 - 1, ir.Height() - this->object_margin - ScaleSpriteTrad(TILE_PIXELS), spec,
|
||||
std::min<int>(_selected_object_view, spec->views - 1));
|
||||
}
|
||||
}
|
||||
if (!spec->IsAvailable()) {
|
||||
GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_INFO: {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
const ObjectClass *objclass = ObjectClass::Get(_object_gui.sel_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type);
|
||||
if (spec == nullptr) break;
|
||||
|
||||
/* Get the extra message for the GUI */
|
||||
if (HasBit(spec->callback_mask, CBM_OBJ_FUND_MORE_TEXT)) {
|
||||
uint16_t callback_res = GetObjectCallback(CBID_OBJECT_FUND_MORE_TEXT, 0, 0, spec, nullptr, INVALID_TILE, _selected_object_view);
|
||||
uint16_t callback_res = GetObjectCallback(CBID_OBJECT_FUND_MORE_TEXT, 0, 0, spec, nullptr, INVALID_TILE, _object_gui.sel_view);
|
||||
if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
|
||||
if (callback_res > 0x400) {
|
||||
ErrorUnknownCallbackResult(spec->grf_prop.grffile->grfid, CBID_OBJECT_FUND_MORE_TEXT, callback_res);
|
||||
@ -407,131 +233,80 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
this->PickerWindow::DrawWidget(r, widget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the specified object class.
|
||||
* @param object_class Object class select.
|
||||
*/
|
||||
void SelectOtherClass(ObjectClassID object_class)
|
||||
void UpdateSelectSize(const ObjectSpec *spec)
|
||||
{
|
||||
_selected_object_class = object_class;
|
||||
ObjectClass *objclass = ObjectClass::Get(object_class);
|
||||
this->GetWidget<NWidgetMatrix>(WID_BO_SELECT_MATRIX)->SetCount(objclass->GetUISpecCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the specified object in #_selected_object_class class.
|
||||
* @param object_index Object index to select, \c -1 means select nothing.
|
||||
*/
|
||||
void SelectOtherObject(int object_index)
|
||||
{
|
||||
_selected_object_index = object_index;
|
||||
if (_selected_object_index != -1) {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
_selected_object_view = std::min<int>(_selected_object_view, spec->views - 1);
|
||||
this->ReInit();
|
||||
} else {
|
||||
_selected_object_view = 0;
|
||||
}
|
||||
|
||||
if (_selected_object_index != -1) {
|
||||
SetObjectToPlaceWnd(SPR_CURSOR_TRANSMITTER, PAL_NONE, HT_RECT | HT_DIAGONAL, this);
|
||||
} else {
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
|
||||
this->UpdateButtons(_selected_object_class, _selected_object_index, _selected_object_view);
|
||||
}
|
||||
|
||||
void UpdateSelectSize()
|
||||
{
|
||||
if (_selected_object_index == -1) {
|
||||
if (spec == nullptr) {
|
||||
SetTileSelectSize(1, 1);
|
||||
ResetObjectToPlace();
|
||||
} else {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
const ObjectSpec *spec = objclass->GetSpec(_selected_object_index);
|
||||
int w = GB(spec->size, HasBit(_selected_object_view, 0) ? 4 : 0, 4);
|
||||
int h = GB(spec->size, HasBit(_selected_object_view, 0) ? 0 : 4, 4);
|
||||
_object_gui.sel_view = std::min<int>(_object_gui.sel_view, spec->views - 1);
|
||||
SetObjectToPlaceWnd(SPR_CURSOR_TRANSMITTER, PAL_NONE, HT_RECT | HT_DIAGONAL, this);
|
||||
int w = GB(spec->size, HasBit(_object_gui.sel_view, 0) ? 4 : 0, 4);
|
||||
int h = GB(spec->size, HasBit(_object_gui.sel_view, 0) ? 0 : 4, 4);
|
||||
SetTileSelectSize(w, h);
|
||||
this->ReInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update buttons to show the selection to the user.
|
||||
* @param object_class The class of the selected object.
|
||||
* @param sel_index Index of the object to select, or \c -1 .
|
||||
* @param sel_view View of the object to select.
|
||||
* @param spec The object spec of the selected object.
|
||||
*/
|
||||
void UpdateButtons(ObjectClassID object_class, int sel_index, uint sel_view)
|
||||
void UpdateButtons(const ObjectSpec *spec)
|
||||
{
|
||||
int view_number, object_number;
|
||||
if (sel_index == -1) {
|
||||
view_number = -1; // If no object selected, also hide the selected view.
|
||||
object_number = -1;
|
||||
} else {
|
||||
view_number = sel_view;
|
||||
ObjectClass *objclass = ObjectClass::Get(object_class);
|
||||
object_number = objclass->GetUIFromIndex(sel_index);
|
||||
}
|
||||
|
||||
this->GetWidget<NWidgetMatrix>(WID_BO_OBJECT_MATRIX)->SetClicked(view_number);
|
||||
this->GetWidget<NWidgetMatrix>(WID_BO_SELECT_MATRIX)->SetClicked(object_number);
|
||||
this->UpdateSelectSize();
|
||||
this->GetWidget<NWidgetMatrix>(WID_BO_OBJECT_MATRIX)->SetClicked(_object_gui.sel_view);
|
||||
this->UpdateSelectSize(spec);
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
|
||||
{
|
||||
this->PickerWindow::OnInvalidateData(data, gui_scope);
|
||||
|
||||
if (!gui_scope) return;
|
||||
|
||||
this->BuildObjectClassesAvailable();
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
this->vscroll->SetCapacityFromWidget(this, WID_BO_CLASS_LIST);
|
||||
if ((data & PickerWindow::PFI_POSITION) != 0) {
|
||||
const auto objclass = ObjectClass::Get(_object_gui.sel_class);
|
||||
const auto spec = objclass->GetSpec(_object_gui.sel_type);
|
||||
_object_gui.sel_view = std::min<int>(_object_gui.sel_view, spec->views - 1);
|
||||
this->UpdateButtons(spec);
|
||||
}
|
||||
}
|
||||
|
||||
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BO_CLASS_LIST: {
|
||||
auto it = this->vscroll->GetScrolledItemFromWidget(this->object_classes, pt.y, this, widget);
|
||||
if (it == this->object_classes.end()) break;
|
||||
|
||||
this->SelectOtherClass(*it);
|
||||
this->SelectFirstAvailableObject(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_SELECT_IMAGE: {
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
int num_clicked = objclass->GetIndexFromUI(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement());
|
||||
if (num_clicked >= 0 && objclass->GetSpec(num_clicked)->IsAvailable()) this->SelectOtherObject(num_clicked);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BO_OBJECT_SPRITE:
|
||||
if (_selected_object_index != -1) {
|
||||
_selected_object_view = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
|
||||
this->SelectOtherObject(_selected_object_index); // Re-select the object for a different view.
|
||||
if (_object_gui.sel_type != MAX_UVALUE(uint16_t)) {
|
||||
_object_gui.sel_view = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
|
||||
this->InvalidateData(PickerWindow::PFI_POSITION);
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
this->PickerWindow::OnClick(pt, widget, click_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
|
||||
{
|
||||
const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index);
|
||||
const ObjectSpec *spec = ObjectClass::Get(_object_gui.sel_class)->GetSpec(_object_gui.sel_type);
|
||||
|
||||
if (spec->size == OBJECT_SIZE_1X1) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT);
|
||||
} else {
|
||||
Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _selected_object_view);
|
||||
Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _object_gui.sel_view);
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,85 +327,14 @@ public:
|
||||
if (TileX(end_tile) == Map::MaxX()) end_tile += TileDiffXY(-1, 0);
|
||||
if (TileY(end_tile) == Map::MaxY()) end_tile += TileDiffXY(0, -1);
|
||||
}
|
||||
const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index);
|
||||
const ObjectSpec *spec = ObjectClass::Get(_object_gui.sel_class)->GetSpec(_object_gui.sel_type);
|
||||
Command<CMD_BUILD_OBJECT_AREA>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER,
|
||||
end_tile, start_tile, spec->Index(), _selected_object_view, (_ctrl_pressed ? true : false));
|
||||
end_tile, start_tile, spec->Index(), _object_gui.sel_view, (_ctrl_pressed ? true : false));
|
||||
}
|
||||
|
||||
void OnPlaceObjectAbort() override
|
||||
{
|
||||
this->UpdateButtons(_selected_object_class, -1, _selected_object_view);
|
||||
}
|
||||
|
||||
EventState OnHotkey(int hotkey) override
|
||||
{
|
||||
switch (hotkey) {
|
||||
case BOHK_FOCUS_FILTER_BOX:
|
||||
this->SetFocusedWidget(WID_BO_FILTER);
|
||||
SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
|
||||
break;
|
||||
|
||||
default:
|
||||
return ES_NOT_HANDLED;
|
||||
}
|
||||
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
void OnEditboxChanged(WidgetID widget) override
|
||||
{
|
||||
if (widget == WID_BO_FILTER) {
|
||||
string_filter.SetFilterTerm(this->filter_editbox.text.buf);
|
||||
this->object_classes.SetFilterState(!string_filter.IsEmpty());
|
||||
this->object_classes.ForceRebuild();
|
||||
this->InvalidateData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the first available object.
|
||||
* @param change_class If true, change the class if no object in the current
|
||||
* class is available.
|
||||
*/
|
||||
void SelectFirstAvailableObject(bool change_class)
|
||||
{
|
||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
||||
|
||||
/* First try to select an object in the selected class. */
|
||||
for (uint i = 0; i < objclass->GetSpecCount(); i++) {
|
||||
const ObjectSpec *spec = objclass->GetSpec(i);
|
||||
if (spec->IsAvailable()) {
|
||||
this->SelectOtherObject(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (change_class) {
|
||||
/* If that fails, select the first available object
|
||||
* from a random class. */
|
||||
for (auto object_class_id : this->object_classes) {
|
||||
ObjectClass *objclass = ObjectClass::Get(object_class_id);
|
||||
for (uint i = 0; i < objclass->GetSpecCount(); i++) {
|
||||
const ObjectSpec *spec = objclass->GetSpec(i);
|
||||
if (spec->IsAvailable()) {
|
||||
this->SelectOtherClass(object_class_id);
|
||||
this->SelectOtherObject(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If all objects are unavailable, select nothing... */
|
||||
if (objclass->GetUISpecCount() == 0) {
|
||||
/* ... but make sure that the class is not empty. */
|
||||
for (auto object_class_id : this->object_classes) {
|
||||
ObjectClass *objclass = ObjectClass::Get(object_class_id);
|
||||
if (objclass->GetUISpecCount() > 0) {
|
||||
this->SelectOtherClass(object_class_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->SelectOtherObject(-1);
|
||||
this->UpdateButtons(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -647,22 +351,10 @@ public:
|
||||
}
|
||||
|
||||
static inline HotkeyList hotkeys{"buildobject", {
|
||||
Hotkey('F', "focus_filter_box", BOHK_FOCUS_FILTER_BOX),
|
||||
Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX),
|
||||
}, BuildObjectGlobalHotkeys};
|
||||
};
|
||||
|
||||
|
||||
Listing BuildObjectWindow::last_sorting = { false, 0 };
|
||||
Filtering BuildObjectWindow::last_filtering = { false, 0 };
|
||||
|
||||
const std::initializer_list<BuildObjectWindow::GUIObjectClassList::SortFunction * const> BuildObjectWindow::sorter_funcs = {
|
||||
&ObjectClassIDSorter,
|
||||
};
|
||||
|
||||
const std::initializer_list<BuildObjectWindow::GUIObjectClassList::FilterFunction * const> BuildObjectWindow::filter_funcs = {
|
||||
&TagNameFilter,
|
||||
};
|
||||
|
||||
static constexpr NWidgetPart _nested_build_object_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
@ -672,41 +364,22 @@ static constexpr NWidgetPart _nested_build_object_widgets[] = {
|
||||
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BO_FILTER), SetFill(1, 0), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BO_CLASS_LIST), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_OBJECT_BUILD_CLASS_TOOLTIP), SetScrollbar(WID_BO_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BO_SCROLLBAR),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidgetFunction(MakePickerClassWidgets),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BO_OBJECT_MATRIX), SetPIP(0, 2, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BO_OBJECT_SPRITE), SetDataTip(0x0, STR_OBJECT_BUILD_PREVIEW_TOOLTIP), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_NAME), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetAlignment(SA_CENTER),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_SIZE), SetDataTip(STR_OBJECT_BUILD_SIZE, STR_NULL), SetAlignment(SA_CENTER),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BO_SELECT_SCROLL),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BO_SELECT_MATRIX), SetPIP(0, 2, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BO_SELECT_IMAGE), SetDataTip(0x0, STR_OBJECT_BUILD_TOOLTIP),
|
||||
SetFill(0, 0), SetResize(0, 0), SetScrollbar(WID_BO_SELECT_SCROLL),
|
||||
EndContainer(),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_SIZE), SetDataTip(STR_OBJECT_BUILD_SIZE, STR_NULL), SetAlignment(SA_CENTER),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BO_INFO), SetFill(1, 0), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BO_INFO), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 0), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BO_SELECT_SCROLL),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidgetFunction(MakePickerTypeWidgets),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@ -722,7 +395,7 @@ static WindowDesc _build_object_desc(
|
||||
Window *ShowBuildObjectPicker()
|
||||
{
|
||||
/* Don't show the place object button when there are no objects to place. */
|
||||
if (ObjectClass::GetUIClassCount() > 0) {
|
||||
if (ObjectPickerCallbacks::instance.IsActive()) {
|
||||
return AllocateWindowDescFront<BuildObjectWindow>(&_build_object_desc, 0);
|
||||
}
|
||||
return nullptr;
|
||||
@ -731,5 +404,5 @@ Window *ShowBuildObjectPicker()
|
||||
/** Reset all data of the object GUI. */
|
||||
void InitializeObjectGui()
|
||||
{
|
||||
_selected_object_class = ObjectClassID::OBJECT_CLASS_BEGIN;
|
||||
_object_gui.sel_class = ObjectClassID::OBJECT_CLASS_BEGIN;
|
||||
}
|
||||
|
465
src/picker_gui.cpp
Normal file
465
src/picker_gui.cpp
Normal file
@ -0,0 +1,465 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file picker_gui.cpp %File for dealing with picker windows */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "core/backup_type.hpp"
|
||||
#include "gui.h"
|
||||
#include "hotkeys.h"
|
||||
#include "picker_gui.h"
|
||||
#include "querystring_gui.h"
|
||||
#include "settings_type.h"
|
||||
#include "sortlist_type.h"
|
||||
#include "sound_func.h"
|
||||
#include "sound_type.h"
|
||||
#include "stringfilter_type.h"
|
||||
#include "strings_func.h"
|
||||
#include "widget_type.h"
|
||||
#include "window_func.h"
|
||||
#include "window_gui.h"
|
||||
#include "window_type.h"
|
||||
#include "zoom_func.h"
|
||||
|
||||
#include "widgets/picker_widget.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/** Sort classes by id. */
|
||||
static bool ClassIDSorter(int const &a, int const &b)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
/** Filter classes by class name. */
|
||||
static bool ClassTagNameFilter(int const *item, PickerFilterData &filter)
|
||||
{
|
||||
filter.ResetState();
|
||||
filter.AddLine(GetString(filter.callbacks->GetClassName(*item)));
|
||||
return filter.GetState();
|
||||
}
|
||||
|
||||
/** Sort types by id. */
|
||||
static bool TypeIDSorter(PickerItem const &a, PickerItem const &b)
|
||||
{
|
||||
int r = a.class_index - b.class_index;
|
||||
if (r == 0) r = a.index - b.index;
|
||||
return r < 0;
|
||||
}
|
||||
|
||||
/** Filter types by class name. */
|
||||
static bool TypeTagNameFilter(PickerItem const *item, PickerFilterData &filter)
|
||||
{
|
||||
filter.ResetState();
|
||||
filter.AddLine(GetString(filter.callbacks->GetTypeName(item->class_index, item->index)));
|
||||
return filter.GetState();
|
||||
}
|
||||
|
||||
static const std::initializer_list<PickerClassList::SortFunction * const> _class_sorter_funcs = { &ClassIDSorter }; ///< Sort functions of the #PickerClassList
|
||||
static const std::initializer_list<PickerClassList::FilterFunction * const> _class_filter_funcs = { &ClassTagNameFilter }; ///< Filter functions of the #PickerClassList.
|
||||
static const std::initializer_list<PickerTypeList::SortFunction * const> _type_sorter_funcs = { TypeIDSorter }; ///< Sort functions of the #PickerTypeList.
|
||||
static const std::initializer_list<PickerTypeList::FilterFunction * const> _type_filter_funcs = { TypeTagNameFilter }; ///< Filter functions of the #PickerTypeList.
|
||||
|
||||
PickerWindow::PickerWindow(WindowDesc *desc, Window *parent, int window_number, PickerCallbacks &callbacks) : PickerWindowBase(desc, parent), callbacks(callbacks),
|
||||
class_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE),
|
||||
type_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
|
||||
{
|
||||
this->window_number = window_number;
|
||||
|
||||
/* Init of nested tree is deferred.
|
||||
* PickerWindow::ConstructWindow must be called by the inheriting window. */
|
||||
}
|
||||
|
||||
void PickerWindow::ConstructWindow()
|
||||
{
|
||||
this->CreateNestedTree();
|
||||
|
||||
/* Test if pickers should be active.*/
|
||||
bool isActive = this->callbacks.IsActive();
|
||||
|
||||
/* Functionality depends on widgets being present, not window class. */
|
||||
this->has_class_picker = isActive && this->GetWidget<NWidgetBase>(WID_PW_CLASS_LIST) != nullptr && this->callbacks.HasClassChoice();
|
||||
this->has_type_picker = isActive && this->GetWidget<NWidgetBase>(WID_PW_TYPE_MATRIX) != nullptr;
|
||||
|
||||
if (this->has_class_picker) {
|
||||
this->GetWidget<NWidgetCore>(WID_PW_CLASS_LIST)->tool_tip = this->callbacks.GetClassTooltip();
|
||||
|
||||
this->querystrings[WID_PW_CLASS_FILTER] = &this->class_editbox;
|
||||
} else {
|
||||
if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_CLASS_SEL); nwid != nullptr) {
|
||||
/* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
|
||||
bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
|
||||
nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
this->class_editbox.cancel_button = QueryString::ACTION_CLEAR;
|
||||
this->class_string_filter.SetFilterTerm(this->class_editbox.text.buf);
|
||||
this->class_string_filter.callbacks = &this->callbacks;
|
||||
|
||||
this->classes.SetListing(this->callbacks.class_last_sorting);
|
||||
this->classes.SetFiltering(this->callbacks.class_last_filtering);
|
||||
this->classes.SetSortFuncs(_class_sorter_funcs);
|
||||
this->classes.SetFilterFuncs(_class_filter_funcs);
|
||||
|
||||
if (this->has_type_picker) {
|
||||
this->GetWidget<NWidgetCore>(WID_PW_TYPE_ITEM)->tool_tip = this->callbacks.GetTypeTooltip();
|
||||
|
||||
auto *matrix = this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX);
|
||||
matrix->SetScrollbar(this->GetScrollbar(WID_PW_TYPE_SCROLL));
|
||||
|
||||
this->querystrings[WID_PW_TYPE_FILTER] = &this->type_editbox;
|
||||
} else {
|
||||
if (auto *nwid = this->GetWidget<NWidgetStacked>(WID_PW_TYPE_SEL); nwid != nullptr) {
|
||||
/* Check the container orientation. MakeNWidgets adds an additional NWID_VERTICAL container so we check the grand-parent. */
|
||||
bool is_vertical = (nwid->parent->parent->type == NWID_VERTICAL);
|
||||
nwid->SetDisplayedPlane(is_vertical ? SZSP_HORIZONTAL : SZSP_VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
this->type_editbox.cancel_button = QueryString::ACTION_CLEAR;
|
||||
this->type_string_filter.SetFilterTerm(this->type_editbox.text.buf);
|
||||
this->type_string_filter.callbacks = &this->callbacks;
|
||||
|
||||
this->types.SetListing(this->callbacks.type_last_sorting);
|
||||
this->types.SetFiltering(this->callbacks.type_last_filtering);
|
||||
this->types.SetSortFuncs(_type_sorter_funcs);
|
||||
this->types.SetFilterFuncs(_type_filter_funcs);
|
||||
|
||||
this->FinishInitNested(this->window_number);
|
||||
|
||||
this->InvalidateData(PFI_CLASS | PFI_TYPE | PFI_POSITION | PFI_VALIDATE);
|
||||
}
|
||||
|
||||
void PickerWindow::Close(int data)
|
||||
{
|
||||
this->callbacks.Close(data);
|
||||
this->PickerWindowBase::Close(data);
|
||||
}
|
||||
|
||||
void PickerWindow::UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
|
||||
{
|
||||
switch (widget) {
|
||||
/* Class picker */
|
||||
case WID_PW_CLASS_LIST:
|
||||
resize.height = GetCharacterHeight(FS_NORMAL) + padding.height;
|
||||
size.height = 5 * resize.height;
|
||||
break;
|
||||
|
||||
/* Type picker */
|
||||
case WID_PW_TYPE_MATRIX:
|
||||
/* At least two items wide. */
|
||||
size.width += resize.width;
|
||||
fill.width = resize.width;
|
||||
fill.height = 1;
|
||||
|
||||
/* Resizing in X direction only at blob size, but at pixel level in Y. */
|
||||
resize.height = 1;
|
||||
break;
|
||||
|
||||
/* Type picker */
|
||||
case WID_PW_TYPE_ITEM:
|
||||
size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
/* Class picker */
|
||||
case WID_PW_CLASS_LIST: {
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
|
||||
const int selected = this->callbacks.GetSelectedClass();
|
||||
const auto vscroll = this->GetScrollbar(WID_PW_CLASS_SCROLL);
|
||||
auto [first, last] = vscroll->GetVisibleRangeIterators(this->classes);
|
||||
for (auto it = first; it != last; ++it) {
|
||||
DrawString(ir, this->callbacks.GetClassName(*it), *it == selected ? TC_WHITE : TC_BLACK);
|
||||
ir.top += this->resize.step_height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Type picker */
|
||||
case WID_PW_TYPE_ITEM: {
|
||||
assert(this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement() < static_cast<int>(this->types.size()));
|
||||
const auto &item = this->types[this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement()];
|
||||
|
||||
DrawPixelInfo tmp_dpi;
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
|
||||
if (FillDrawPixelInfo(&tmp_dpi, ir)) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT);
|
||||
int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM);
|
||||
|
||||
this->callbacks.DrawType(x, y, item.class_index, item.index);
|
||||
}
|
||||
|
||||
if (!this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
|
||||
GfxFillRect(ir, GetColourGradient(COLOUR_GREY, SHADE_DARKER), FILLRECT_CHECKER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_PW_TYPE_NAME:
|
||||
DrawString(r, this->callbacks.GetTypeName(this->callbacks.GetSelectedClass(), this->callbacks.GetSelectedType()), TC_ORANGE, SA_CENTER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PickerWindow::OnResize()
|
||||
{
|
||||
if (this->has_class_picker) {
|
||||
this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCapacityFromWidget(this, WID_PW_CLASS_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
void PickerWindow::OnClick(Point pt, WidgetID widget, int)
|
||||
{
|
||||
switch (widget) {
|
||||
/* Class Picker */
|
||||
case WID_PW_CLASS_LIST: {
|
||||
const auto vscroll = this->GetWidget<NWidgetScrollbar>(WID_PW_CLASS_SCROLL);
|
||||
auto it = vscroll->GetScrolledItemFromWidget(this->classes, pt.y, this, WID_PW_CLASS_LIST);
|
||||
if (it == this->classes.end()) return;
|
||||
|
||||
if (this->callbacks.GetSelectedClass() != *it) {
|
||||
this->callbacks.SetSelectedClass(*it);
|
||||
this->InvalidateData(PFI_TYPE | PFI_POSITION | PFI_VALIDATE);
|
||||
}
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Type Picker */
|
||||
case WID_PW_TYPE_ITEM: {
|
||||
int sel = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
|
||||
assert(sel < (int)this->types.size());
|
||||
const auto &item = this->types[sel];
|
||||
if (this->callbacks.IsTypeAvailable(item.class_index, item.index)) {
|
||||
this->callbacks.SetSelectedClass(item.class_index);
|
||||
this->callbacks.SetSelectedType(item.index);
|
||||
this->InvalidateData(PFI_POSITION);
|
||||
}
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PickerWindow::OnInvalidateData(int data, bool gui_scope)
|
||||
{
|
||||
if (!gui_scope) return;
|
||||
|
||||
if ((data & PFI_CLASS) != 0) this->classes.ForceRebuild();
|
||||
if ((data & PFI_TYPE) != 0) this->types.ForceRebuild();
|
||||
|
||||
this->BuildPickerClassList();
|
||||
if ((data & PFI_VALIDATE) != 0) this->EnsureSelectedClassIsValid();
|
||||
if ((data & PFI_POSITION) != 0) this->EnsureSelectedClassIsVisible();
|
||||
|
||||
this->BuildPickerTypeList();
|
||||
if ((data & PFI_VALIDATE) != 0) this->EnsureSelectedTypeIsValid();
|
||||
if ((data & PFI_POSITION) != 0) this->EnsureSelectedTypeIsVisible();
|
||||
}
|
||||
|
||||
EventState PickerWindow::OnHotkey(int hotkey)
|
||||
{
|
||||
switch (hotkey) {
|
||||
case PCWHK_FOCUS_FILTER_BOX:
|
||||
/* Cycle between the two edit boxes. */
|
||||
if (this->has_type_picker && (this->nested_focus == nullptr || this->nested_focus->index != WID_PW_TYPE_FILTER)) {
|
||||
this->SetFocusedWidget(WID_PW_TYPE_FILTER);
|
||||
} else if (this->has_class_picker && (this->nested_focus == nullptr || this->nested_focus->index != WID_PW_CLASS_FILTER)) {
|
||||
this->SetFocusedWidget(WID_PW_CLASS_FILTER);
|
||||
}
|
||||
SetFocusedWindow(this);
|
||||
return ES_HANDLED;
|
||||
|
||||
default:
|
||||
return ES_NOT_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
void PickerWindow::OnEditboxChanged(WidgetID wid)
|
||||
{
|
||||
switch (wid) {
|
||||
case WID_PW_CLASS_FILTER:
|
||||
this->class_string_filter.SetFilterTerm(this->class_editbox.text.buf);
|
||||
this->classes.SetFilterState(!class_string_filter.IsEmpty());
|
||||
this->InvalidateData(PFI_CLASS);
|
||||
break;
|
||||
|
||||
case WID_PW_TYPE_FILTER:
|
||||
this->type_string_filter.SetFilterTerm(this->type_editbox.text.buf);
|
||||
this->types.SetFilterState(!type_string_filter.IsEmpty());
|
||||
this->InvalidateData(PFI_TYPE);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds the filter list of classes. */
|
||||
void PickerWindow::BuildPickerClassList()
|
||||
{
|
||||
if (!this->classes.NeedRebuild()) return;
|
||||
|
||||
int count = this->callbacks.GetClassCount();
|
||||
|
||||
this->classes.clear();
|
||||
this->classes.reserve(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (this->callbacks.GetClassName(i) == INVALID_STRING_ID) continue;
|
||||
this->classes.emplace_back(i);
|
||||
}
|
||||
|
||||
this->classes.Filter(this->class_string_filter);
|
||||
this->classes.RebuildDone();
|
||||
this->classes.Sort();
|
||||
|
||||
if (!this->has_class_picker) return;
|
||||
this->GetScrollbar(WID_PW_CLASS_SCROLL)->SetCount(this->classes.size());
|
||||
}
|
||||
|
||||
void PickerWindow::EnsureSelectedClassIsValid()
|
||||
{
|
||||
int class_index = this->callbacks.GetSelectedClass();
|
||||
if (std::binary_search(std::begin(this->classes), std::end(this->classes), class_index)) return;
|
||||
|
||||
class_index = this->classes.front();
|
||||
this->callbacks.SetSelectedClass(class_index);
|
||||
}
|
||||
|
||||
void PickerWindow::EnsureSelectedClassIsVisible()
|
||||
{
|
||||
if (!this->has_class_picker) return;
|
||||
if (this->classes.empty()) return;
|
||||
|
||||
auto it = std::find(std::begin(this->classes), std::end(this->classes), this->callbacks.GetSelectedClass());
|
||||
if (it == std::end(this->classes)) return;
|
||||
|
||||
int pos = static_cast<int>(std::distance(std::begin(this->classes), it));
|
||||
this->GetScrollbar(WID_PW_CLASS_SCROLL)->ScrollTowards(pos);
|
||||
}
|
||||
|
||||
/** Builds the filter list of types. */
|
||||
void PickerWindow::BuildPickerTypeList()
|
||||
{
|
||||
if (!this->types.NeedRebuild()) return;
|
||||
|
||||
this->types.clear();
|
||||
int cls_id = this->callbacks.GetSelectedClass();
|
||||
|
||||
{
|
||||
/* Add types in only the selected class. */
|
||||
if (cls_id >= 0 && cls_id < this->callbacks.GetClassCount()) {
|
||||
int count = this->callbacks.GetTypeCount(cls_id);
|
||||
this->types.reserve(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (this->callbacks.GetTypeName(cls_id, i) == INVALID_STRING_ID) continue;
|
||||
this->types.emplace_back(this->callbacks.GetPickerItem(cls_id, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->types.Filter(this->type_string_filter);
|
||||
this->types.RebuildDone();
|
||||
this->types.Sort();
|
||||
|
||||
if (!this->has_type_picker) return;
|
||||
this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetCount(static_cast<int>(this->types.size()));
|
||||
}
|
||||
|
||||
void PickerWindow::EnsureSelectedTypeIsValid()
|
||||
{
|
||||
int class_index = this->callbacks.GetSelectedClass();
|
||||
int index = this->callbacks.GetSelectedType();
|
||||
if (std::any_of(std::begin(this->types), std::end(this->types), [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; })) return;
|
||||
|
||||
class_index = this->types.front().class_index;
|
||||
index = this->types.front().index;
|
||||
this->callbacks.SetSelectedClass(class_index);
|
||||
this->callbacks.SetSelectedType(index);
|
||||
}
|
||||
|
||||
void PickerWindow::EnsureSelectedTypeIsVisible()
|
||||
{
|
||||
if (!this->has_type_picker) return;
|
||||
if (this->types.empty()) {
|
||||
this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
int class_index = this->callbacks.GetSelectedClass();
|
||||
int index = this->callbacks.GetSelectedType();
|
||||
|
||||
auto it = std::find_if(std::begin(this->types), std::end(this->types), [class_index, index](const auto &item) { return item.class_index == class_index && item.index == index; });
|
||||
if (it == std::end(this->types)) return;
|
||||
|
||||
int pos = static_cast<int>(std::distance(std::begin(this->types), it));
|
||||
this->GetWidget<NWidgetMatrix>(WID_PW_TYPE_MATRIX)->SetClicked(pos);
|
||||
}
|
||||
|
||||
/** Create nested widgets for the class picker widgets. */
|
||||
std::unique_ptr<NWidgetBase> MakePickerClassWidgets()
|
||||
{
|
||||
static constexpr NWidgetPart picker_class_widgets[] = {
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_CLASS_SEL),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_PW_CLASS_FILTER), SetMinimalSize(144, 0), SetPadding(2), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_PW_CLASS_LIST), SetFill(1, 1), SetResize(1, 1), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_PW_CLASS_SCROLL),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_CLASS_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
return MakeNWidgets(std::begin(picker_class_widgets), std::end(picker_class_widgets), nullptr);
|
||||
}
|
||||
|
||||
/** Create nested widgets for the type picker widgets. */
|
||||
std::unique_ptr<NWidgetBase> MakePickerTypeWidgets()
|
||||
{
|
||||
static constexpr NWidgetPart picker_type_widgets[] = {
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_PW_TYPE_SEL),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_EDITBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_FILTER), SetPadding(2), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_PW_TYPE_SCROLL),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_PW_TYPE_MATRIX), SetPIP(0, 2, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_PW_TYPE_ITEM), SetScrollbar(WID_PW_TYPE_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_PW_TYPE_SCROLL),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_PW_TYPE_NAME), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(1, 0), SetFill(1, 0), SetMinimalTextLines(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN, WID_PW_TYPE_RESIZE),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
return MakeNWidgets(std::begin(picker_type_widgets), std::end(picker_type_widgets), nullptr);
|
||||
}
|
181
src/picker_gui.h
Normal file
181
src/picker_gui.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file picker_gui.h Functions/types etc. related to the picker GUI. */
|
||||
|
||||
#ifndef PICKER_GUI_H
|
||||
#define PICKER_GUI_H
|
||||
|
||||
#include "querystring_gui.h"
|
||||
#include "sortlist_type.h"
|
||||
#include "stringfilter_type.h"
|
||||
#include "strings_type.h"
|
||||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "window_gui.h"
|
||||
#include "window_type.h"
|
||||
|
||||
struct PickerItem {
|
||||
uint32_t grfid;
|
||||
uint16_t local_id;
|
||||
int class_index;
|
||||
int index;
|
||||
|
||||
inline auto operator<=>(const PickerItem &other) const
|
||||
{
|
||||
if (auto cmp = this->grfid <=> other.grfid; cmp != 0) return cmp;
|
||||
return this->local_id <=> other.local_id;
|
||||
}
|
||||
};
|
||||
|
||||
/** Class for PickerClassWindow to collect information and retain state. */
|
||||
class PickerCallbacks {
|
||||
public:
|
||||
virtual ~PickerCallbacks() {}
|
||||
virtual void Close(int) { }
|
||||
|
||||
/** Should picker class/type selection be enabled? */
|
||||
virtual bool IsActive() const = 0;
|
||||
/** Are there multiple classes to chose from? */
|
||||
virtual bool HasClassChoice() const = 0;
|
||||
|
||||
/* Class callbacks */
|
||||
/** Get the tooltip string for the class list. */
|
||||
virtual StringID GetClassTooltip() const = 0;
|
||||
/** Get the number of classes. @note Used only to estimate space requirements. */
|
||||
virtual int GetClassCount() const = 0;
|
||||
/** Get the index of the selected class. */
|
||||
virtual int GetSelectedClass() const = 0;
|
||||
/** Set the selected class. */
|
||||
virtual void SetSelectedClass(int id) const = 0;
|
||||
/** Get the name of a class. */
|
||||
virtual StringID GetClassName(int id) const = 0;
|
||||
|
||||
/* Type callbacks */
|
||||
/** Get the tooltip string for the type grid. */
|
||||
virtual StringID GetTypeTooltip() const = 0;
|
||||
/** Get the number of types in a class. @note Used only to estimate space requirements. */
|
||||
virtual int GetTypeCount(int cls_id) const = 0;
|
||||
|
||||
/** Get the selected type. */
|
||||
virtual int GetSelectedType() const = 0;
|
||||
/** Set the selected type. */
|
||||
virtual void SetSelectedType(int id) const = 0;
|
||||
/** Get data about an item. */
|
||||
virtual PickerItem GetPickerItem(int cls_id, int id) const = 0;
|
||||
/** Get the item of a type. */
|
||||
virtual StringID GetTypeName(int cls_id, int id) const = 0;
|
||||
/** Test if an item is currently buildable. */
|
||||
virtual bool IsTypeAvailable(int cls_id, int id) const = 0;
|
||||
/** Draw preview image of an item. */
|
||||
virtual void DrawType(int x, int y, int cls_id, int id) const = 0;
|
||||
|
||||
Listing class_last_sorting = { false, 0 }; ///< Default sorting of #PickerClassList.
|
||||
Filtering class_last_filtering = { false, 0 }; ///< Default filtering of #PickerClassList.
|
||||
|
||||
Listing type_last_sorting = { false, 0 }; ///< Default sorting of #PickerTypeList.
|
||||
Filtering type_last_filtering = { false, 0 }; ///< Default filtering of #PickerTypeList.
|
||||
};
|
||||
|
||||
/** Helper for PickerCallbacks when the class system is based on NewGRFClass. */
|
||||
template <typename T>
|
||||
class PickerCallbacksNewGRFClass : public PickerCallbacks {
|
||||
public:
|
||||
inline typename T::index_type GetClassIndex(int cls_id) const { return static_cast<typename T::index_type>(cls_id); }
|
||||
inline const T *GetClass(int cls_id) const { return T::Get(this->GetClassIndex(cls_id)); }
|
||||
inline const typename T::spec_type *GetSpec(int cls_id, int id) const { return this->GetClass(cls_id)->GetSpec(id); }
|
||||
|
||||
bool HasClassChoice() const override { return T::GetUIClassCount() > 1; }
|
||||
|
||||
int GetClassCount() const override { return T::GetClassCount(); }
|
||||
int GetTypeCount(int cls_id) const override { return this->GetClass(cls_id)->GetSpecCount(); }
|
||||
|
||||
PickerItem GetPickerItem(const typename T::spec_type *spec, int cls_id = -1, int id = -1) const
|
||||
{
|
||||
if (spec == nullptr) return {0, 0, cls_id, id};
|
||||
return {spec->grf_prop.grffile == nullptr ? 0 : spec->grf_prop.grffile->grfid, spec->grf_prop.local_id, spec->class_index, spec->index};
|
||||
}
|
||||
|
||||
PickerItem GetPickerItem(int cls_id, int id) const override
|
||||
{
|
||||
return GetPickerItem(GetClass(cls_id)->GetSpec(id), cls_id, id);
|
||||
}
|
||||
};
|
||||
|
||||
struct PickerFilterData : StringFilter {
|
||||
const PickerCallbacks *callbacks; ///< Callbacks for filter functions to access to callbacks.
|
||||
};
|
||||
|
||||
using PickerClassList = GUIList<int, std::nullptr_t, PickerFilterData &>; ///< GUIList holding classes to display.
|
||||
using PickerTypeList = GUIList<PickerItem, std::nullptr_t, PickerFilterData &>; ///< GUIList holding classes/types to display.
|
||||
|
||||
class PickerWindow : public PickerWindowBase {
|
||||
public:
|
||||
enum PickerFilterInvalidation {
|
||||
PFI_CLASS = 1U << 0, ///< Refresh the class list.
|
||||
PFI_TYPE = 1U << 1, ///< Refresh the type list.
|
||||
PFI_POSITION = 1U << 2, ///< Update scroll positions.
|
||||
PFI_VALIDATE = 1U << 3, ///< Validate selected item.
|
||||
};
|
||||
|
||||
static const int PREVIEW_WIDTH = 64; ///< Width of each preview button.
|
||||
static const int PREVIEW_HEIGHT = 48; ///< Height of each preview button.
|
||||
static const int PREVIEW_LEFT = 31; ///< Offset from left edge to draw preview.
|
||||
static const int PREVIEW_BOTTOM = 31; ///< Offset from bottom edge to draw preview.
|
||||
|
||||
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.
|
||||
|
||||
bool has_class_picker = false; ///< Set if this window has a class picker 'component'.
|
||||
bool has_type_picker = false; ///< Set if this window has a type picker 'component'.
|
||||
|
||||
PickerWindow(WindowDesc *desc, Window *parent, int window_number, PickerCallbacks &callbacks);
|
||||
void Close(int data = 0) override;
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override;
|
||||
void DrawWidget(const Rect &r, WidgetID widget) const override;
|
||||
void OnResize() override;
|
||||
void OnClick(Point pt, WidgetID widget, int click_count) override;
|
||||
void OnInvalidateData(int data = 0, bool gui_scope = true) override;
|
||||
EventState OnHotkey(int hotkey) override;
|
||||
void OnEditboxChanged(WidgetID wid) override;
|
||||
|
||||
/** Enum referring to the Hotkeys in the picker window */
|
||||
enum PickerClassWindowHotkeys {
|
||||
PCWHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
|
||||
};
|
||||
|
||||
protected:
|
||||
void ConstructWindow();
|
||||
|
||||
PickerCallbacks &callbacks;
|
||||
|
||||
private:
|
||||
PickerClassList classes; ///< List of classes.
|
||||
PickerFilterData class_string_filter;
|
||||
QueryString class_editbox; ///< Filter editbox.
|
||||
|
||||
void BuildPickerClassList();
|
||||
void EnsureSelectedClassIsValid();
|
||||
void EnsureSelectedClassIsVisible();
|
||||
|
||||
PickerTypeList types; ///< List of types.
|
||||
PickerFilterData type_string_filter;
|
||||
QueryString type_editbox; ///< Filter editbox
|
||||
|
||||
void BuildPickerTypeList();
|
||||
void EnsureSelectedTypeIsValid();
|
||||
void EnsureSelectedTypeIsVisible();
|
||||
|
||||
IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) {
|
||||
this->SetDirty();
|
||||
}};
|
||||
};
|
||||
|
||||
class NWidgetBase;
|
||||
std::unique_ptr<NWidgetBase> MakePickerClassWidgets();
|
||||
std::unique_ptr<NWidgetBase> MakePickerTypeWidgets();
|
||||
|
||||
#endif /* PICKER_GUI_H */
|
829
src/rail_gui.cpp
829
src/rail_gui.cpp
File diff suppressed because it is too large
Load Diff
706
src/road_gui.cpp
706
src/road_gui.cpp
@ -36,10 +36,7 @@
|
||||
#include "road_cmd.h"
|
||||
#include "tunnelbridge_cmd.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
#include "querystring_gui.h"
|
||||
#include "sortlist_type.h"
|
||||
#include "stringfilter_type.h"
|
||||
#include "string_func.h"
|
||||
#include "picker_gui.h"
|
||||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
|
||||
@ -64,40 +61,43 @@ static RoadType _cur_roadtype;
|
||||
|
||||
static DiagDirection _road_depot_orientation;
|
||||
|
||||
struct RoadStopGUISettings {
|
||||
DiagDirection orientation;
|
||||
|
||||
RoadStopClassID roadstop_class;
|
||||
uint16_t roadstop_type;
|
||||
uint16_t roadstop_count;
|
||||
struct RoadStopPickerSelection {
|
||||
RoadStopClassID sel_class; ///< Selected road stop class.
|
||||
uint16_t sel_type; ///< Selected road stop type within the class.
|
||||
DiagDirection orientation; ///< Selected orientation of the road stop.
|
||||
};
|
||||
static RoadStopGUISettings _roadstop_gui_settings;
|
||||
static RoadStopPickerSelection _roadstop_gui;
|
||||
|
||||
static bool IsRoadStopEverAvailable(const RoadStopSpec *spec, StationType type)
|
||||
{
|
||||
if (spec == nullptr) return true;
|
||||
|
||||
if (HasBit(spec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false;
|
||||
if (HasBit(spec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false;
|
||||
|
||||
switch (spec->stop_type) {
|
||||
case ROADSTOPTYPE_ALL: return true;
|
||||
case ROADSTOPTYPE_PASSENGER: return type == STATION_BUS;
|
||||
case ROADSTOPTYPE_FREIGHT: return type == STATION_TRUCK;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a road stop type can be built.
|
||||
* @return true if building is allowed.
|
||||
*/
|
||||
static bool IsRoadStopAvailable(const RoadStopSpec *roadstopspec, StationType type)
|
||||
static bool IsRoadStopAvailable(const RoadStopSpec *spec, StationType type)
|
||||
{
|
||||
if (roadstopspec == nullptr) return true;
|
||||
if (spec == nullptr) return true;
|
||||
if (IsRoadStopEverAvailable(spec, type)) return true;
|
||||
|
||||
if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false;
|
||||
if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false;
|
||||
if (!HasBit(spec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true;
|
||||
|
||||
if (roadstopspec->stop_type != ROADSTOPTYPE_ALL) {
|
||||
switch (type) {
|
||||
case STATION_BUS: if (roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER) return false; break;
|
||||
case STATION_TRUCK: if (roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT) return false; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true;
|
||||
|
||||
uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, _cur_roadtype, type, 0);
|
||||
uint16_t cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, spec, nullptr, INVALID_TILE, _cur_roadtype, type, 0);
|
||||
if (cb_res == CALLBACK_FAILED) return true;
|
||||
|
||||
return Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res);
|
||||
return Convert8bitBooleanCallback(spec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res);
|
||||
}
|
||||
|
||||
void CcPlaySound_CONSTRUCTION_OTHER(Commands, const CommandCost &result, TileIndex tile)
|
||||
@ -217,11 +217,11 @@ void CcRoadStop(Commands, const CommandCost &result, TileIndex tile, uint8_t wid
|
||||
static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg)
|
||||
{
|
||||
TileArea ta(start_tile, end_tile);
|
||||
DiagDirection ddir = _roadstop_gui_settings.orientation;
|
||||
DiagDirection ddir = _roadstop_gui.orientation;
|
||||
bool drive_through = ddir >= DIAGDIR_END;
|
||||
if (drive_through) ddir = static_cast<DiagDirection>(ddir - DIAGDIR_END); // Adjust picker result to actual direction.
|
||||
RoadStopClassID spec_class = _roadstop_gui_settings.roadstop_class;
|
||||
uint16_t spec_index = _roadstop_gui_settings.roadstop_type;
|
||||
RoadStopClassID spec_class = _roadstop_gui.sel_class;
|
||||
uint16_t spec_index = _roadstop_gui.sel_type;
|
||||
|
||||
auto proc = [=](bool test, StationID to_join) -> bool {
|
||||
if (test) {
|
||||
@ -245,8 +245,8 @@ static void PlaceRoad_BusStation(TileIndex tile)
|
||||
if (_remove_button_clicked) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP);
|
||||
} else {
|
||||
if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP);
|
||||
if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP);
|
||||
} else {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP);
|
||||
}
|
||||
@ -263,8 +263,8 @@ static void PlaceRoad_TruckStation(TileIndex tile)
|
||||
if (_remove_button_clicked) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP);
|
||||
} else {
|
||||
if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
|
||||
if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
|
||||
} else {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
|
||||
}
|
||||
@ -708,7 +708,7 @@ struct BuildRoadToolbarWindow : Window {
|
||||
|
||||
case DDSP_BUILD_BUSSTOP:
|
||||
case DDSP_REMOVE_BUSSTOP:
|
||||
if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_BUS, _cur_roadtype)) {
|
||||
if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui.sel_class), ROADSTOP_BUS, _cur_roadtype)) {
|
||||
if (_remove_button_clicked) {
|
||||
TileArea ta(start_tile, end_tile);
|
||||
Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_BUS, _ctrl_pressed);
|
||||
@ -720,7 +720,7 @@ struct BuildRoadToolbarWindow : Window {
|
||||
|
||||
case DDSP_BUILD_TRUCKSTOP:
|
||||
case DDSP_REMOVE_TRUCKSTOP:
|
||||
if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_TRUCK, _cur_roadtype)) {
|
||||
if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui.sel_class), ROADSTOP_TRUCK, _cur_roadtype)) {
|
||||
if (_remove_button_clicked) {
|
||||
TileArea ta(start_tile, end_tile);
|
||||
Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_TRUCK, _ctrl_pressed);
|
||||
@ -1093,101 +1093,126 @@ static void ShowRoadDepotPicker(Window *parent)
|
||||
new BuildRoadDepotWindow(&_build_road_depot_desc, parent);
|
||||
}
|
||||
|
||||
/** Enum referring to the Hotkeys in the build road stop window */
|
||||
enum BuildRoadStopHotkeys {
|
||||
BROSHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
|
||||
template <RoadStopType roadstoptype>
|
||||
class RoadStopPickerCallbacks : public PickerCallbacksNewGRFClass<RoadStopClass> {
|
||||
public:
|
||||
StringID GetClassTooltip() const override;
|
||||
StringID GetTypeTooltip() const override;
|
||||
|
||||
bool IsActive() const override
|
||||
{
|
||||
for (const auto &cls : RoadStopClass::Classes()) {
|
||||
if (IsWaypointClass(cls)) continue;
|
||||
for (const auto *spec : cls.Specs()) {
|
||||
if (spec == nullptr) continue;
|
||||
if (roadstoptype == ROADSTOP_TRUCK && spec->stop_type != ROADSTOPTYPE_FREIGHT && spec->stop_type != ROADSTOPTYPE_ALL) continue;
|
||||
if (roadstoptype == ROADSTOP_BUS && spec->stop_type != ROADSTOPTYPE_PASSENGER && spec->stop_type != ROADSTOPTYPE_ALL) continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsClassChoice(const RoadStopClass &cls)
|
||||
{
|
||||
return !IsWaypointClass(cls) && GetIfClassHasNewStopsByType(&cls, roadstoptype, _cur_roadtype);
|
||||
}
|
||||
|
||||
bool HasClassChoice() const override
|
||||
{
|
||||
return std::count_if(std::begin(RoadStopClass::Classes()), std::end(RoadStopClass::Classes()), IsClassChoice);
|
||||
}
|
||||
|
||||
int GetSelectedClass() const override { return _roadstop_gui.sel_class; }
|
||||
void SetSelectedClass(int id) const override { _roadstop_gui.sel_class = this->GetClassIndex(id); }
|
||||
|
||||
StringID GetClassName(int id) const override
|
||||
{
|
||||
const auto *rsc = this->GetClass(id);
|
||||
if (!IsClassChoice(*rsc)) return INVALID_STRING_ID;
|
||||
return rsc->name;
|
||||
}
|
||||
|
||||
int GetSelectedType() const override { return _roadstop_gui.sel_type; }
|
||||
void SetSelectedType(int id) const override { _roadstop_gui.sel_type = id; }
|
||||
|
||||
StringID GetTypeName(int cls_id, int id) const override
|
||||
{
|
||||
const auto *spec = this->GetSpec(cls_id, id);
|
||||
if (!IsRoadStopEverAvailable(spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK)) return INVALID_STRING_ID;
|
||||
return (spec == nullptr) ? STR_STATION_CLASS_DFLT_ROADSTOP : spec->name;
|
||||
}
|
||||
|
||||
bool IsTypeAvailable(int cls_id, int id) const override
|
||||
{
|
||||
const auto *spec = this->GetSpec(cls_id, id);
|
||||
return IsRoadStopAvailable(spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK);
|
||||
}
|
||||
|
||||
void DrawType(int x, int y, int cls_id, int id) const override
|
||||
{
|
||||
const auto *spec = this->GetSpec(cls_id, id);
|
||||
if (spec == nullptr) {
|
||||
StationPickerDrawSprite(x, y, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui.orientation);
|
||||
} else {
|
||||
DiagDirection orientation = _roadstop_gui.orientation;
|
||||
if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END;
|
||||
DrawRoadStopTile(x, y, _cur_roadtype, spec, roadstoptype == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK, (uint8_t)orientation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
template <> StringID RoadStopPickerCallbacks<ROADSTOP_BUS>::GetClassTooltip() const { return STR_PICKER_ROADSTOP_BUS_CLASS_TOOLTIP; }
|
||||
template <> StringID RoadStopPickerCallbacks<ROADSTOP_BUS>::GetTypeTooltip() const { return STR_PICKER_ROADSTOP_BUS_TYPE_TOOLTIP; }
|
||||
|
||||
template <> StringID RoadStopPickerCallbacks<ROADSTOP_TRUCK>::GetClassTooltip() const { return STR_PICKER_ROADSTOP_TRUCK_CLASS_TOOLTIP; }
|
||||
template <> StringID RoadStopPickerCallbacks<ROADSTOP_TRUCK>::GetTypeTooltip() const { return STR_PICKER_ROADSTOP_TRUCK_TYPE_TOOLTIP; }
|
||||
|
||||
static RoadStopPickerCallbacks<ROADSTOP_BUS> _bus_callback_instance;
|
||||
static RoadStopPickerCallbacks<ROADSTOP_TRUCK> _truck_callback_instance;
|
||||
|
||||
static PickerCallbacks &GetRoadStopPickerCallbacks(RoadStopType rs)
|
||||
{
|
||||
return rs == ROADSTOP_BUS ? static_cast<PickerCallbacks &>(_bus_callback_instance) : static_cast<PickerCallbacks &>(_truck_callback_instance);
|
||||
}
|
||||
|
||||
struct BuildRoadStationWindow : public PickerWindow {
|
||||
private:
|
||||
RoadStopType roadStopType; ///< The RoadStopType for this Window.
|
||||
uint line_height; ///< Height of a single line in the newstation selection matrix.
|
||||
uint coverage_height; ///< Height of the coverage texts.
|
||||
Scrollbar *vscrollList; ///< Vertical scrollbar of the new station list.
|
||||
Scrollbar *vscrollMatrix; ///< Vertical scrollbar of the station picker matrix.
|
||||
|
||||
typedef GUIList<RoadStopClassID, std::nullptr_t, StringFilter &> GUIRoadStopClassList; ///< Type definition for the list to hold available road stop classes.
|
||||
|
||||
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.
|
||||
|
||||
static Listing last_sorting; ///< Default sorting of #GUIRoadStopClassList.
|
||||
static Filtering last_filtering; ///< Default filtering of #GUIRoadStopClassList.
|
||||
static const std::initializer_list<GUIRoadStopClassList::SortFunction * const> sorter_funcs; ///< Sort functions of the #GUIRoadStopClassList.
|
||||
static const std::initializer_list<GUIRoadStopClassList::FilterFunction * const> filter_funcs; ///< Filter functions of the #GUIRoadStopClassList.
|
||||
GUIRoadStopClassList roadstop_classes; ///< Available road stop classes.
|
||||
StringFilter string_filter; ///< Filter for available road stop classes.
|
||||
QueryString filter_editbox; ///< Filter editbox.
|
||||
|
||||
void EnsureSelectedClassIsVisible()
|
||||
{
|
||||
uint pos = 0;
|
||||
for (auto rs_class : this->roadstop_classes) {
|
||||
if (rs_class == _roadstop_gui_settings.roadstop_class) break;
|
||||
pos++;
|
||||
}
|
||||
this->vscrollList->SetCount(this->roadstop_classes.size());
|
||||
this->vscrollList->ScrollTowards(pos);
|
||||
}
|
||||
|
||||
void CheckOrientationValid()
|
||||
{
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
|
||||
|
||||
/* Raise and lower to ensure the correct widget is lowered after changing displayed orientation plane. */
|
||||
if (RoadTypeIsRoad(_cur_roadtype)) {
|
||||
this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_AVAILABLE_ORIENTATIONS)->SetDisplayedPlane((spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) ? 1 : 0);
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
}
|
||||
|
||||
if (_roadstop_gui_settings.orientation >= DIAGDIR_END) return;
|
||||
if (_roadstop_gui.orientation >= DIAGDIR_END) return;
|
||||
|
||||
if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) {
|
||||
this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
_roadstop_gui_settings.orientation = DIAGDIR_END;
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
_roadstop_gui.orientation = DIAGDIR_END;
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindowBase(desc, parent), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
|
||||
BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindow(desc, parent, TRANSPORT_ROAD, GetRoadStopPickerCallbacks(rs))
|
||||
{
|
||||
this->coverage_height = 2 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
|
||||
this->vscrollList = nullptr;
|
||||
this->vscrollMatrix = nullptr;
|
||||
this->roadStopType = rs;
|
||||
bool newstops = GetIfNewStopsByType(rs, _cur_roadtype);
|
||||
|
||||
this->CreateNestedTree();
|
||||
|
||||
/* Hide the station class filter if no stations other than the default one are available. */
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_DEFSIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_FILTER_CONTAINER)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_ADDITIONS)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_ORIENTATION)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_TYPE_SEL)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_MATRIX)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_RESIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE);
|
||||
if (newstops) {
|
||||
this->vscrollList = this->GetScrollbar(WID_BROS_NEWST_SCROLL);
|
||||
this->vscrollMatrix = this->GetScrollbar(WID_BROS_MATRIX_SCROLL);
|
||||
|
||||
this->querystrings[WID_BROS_FILTER_EDITBOX] = &this->filter_editbox;
|
||||
this->roadstop_classes.SetListing(this->last_sorting);
|
||||
this->roadstop_classes.SetFiltering(this->last_filtering);
|
||||
this->roadstop_classes.SetSortFuncs(this->sorter_funcs);
|
||||
this->roadstop_classes.SetFilterFuncs(this->filter_funcs);
|
||||
}
|
||||
|
||||
this->roadstop_classes.ForceRebuild();
|
||||
BuildRoadStopClassesAvailable();
|
||||
|
||||
/* Trams don't have non-drivethrough stations */
|
||||
if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui_settings.orientation < DIAGDIR_END) {
|
||||
_roadstop_gui_settings.orientation = DIAGDIR_END;
|
||||
if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui.orientation < DIAGDIR_END) {
|
||||
_roadstop_gui.orientation = DIAGDIR_END;
|
||||
}
|
||||
this->ConstructWindow();
|
||||
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype);
|
||||
this->GetWidget<NWidgetCore>(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs];
|
||||
|
||||
@ -1195,130 +1220,24 @@ public:
|
||||
this->GetWidget<NWidgetCore>(i)->tool_tip = rti->strings.picker_tooltip[rs];
|
||||
}
|
||||
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
this->LowerWidget(WID_BROS_LT_OFF + _settings_client.gui.station_show_coverage);
|
||||
|
||||
this->FinishInitNested(TRANSPORT_ROAD);
|
||||
|
||||
this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION;
|
||||
if (!newstops || _roadstop_gui_settings.roadstop_class >= RoadStopClass::GetClassCount()) {
|
||||
/* There's no new stops available or the list has reduced in size.
|
||||
* Now, set the default road stops as selected. */
|
||||
_roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT;
|
||||
_roadstop_gui_settings.roadstop_type = 0;
|
||||
}
|
||||
if (newstops) {
|
||||
/* The currently selected class doesn't have any stops for this RoadStopType, reset the selection. */
|
||||
if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), rs, _cur_roadtype)) {
|
||||
_roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT;
|
||||
_roadstop_gui_settings.roadstop_type = 0;
|
||||
}
|
||||
_roadstop_gui_settings.roadstop_count = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpecCount();
|
||||
_roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, _roadstop_gui_settings.roadstop_count - 1);
|
||||
|
||||
/* Reset back to default class if the previously selected class is not available for this road stop type. */
|
||||
if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), roadStopType, _cur_roadtype)) {
|
||||
_roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT;
|
||||
}
|
||||
|
||||
this->SelectFirstAvailableTypeIfUnavailable();
|
||||
|
||||
NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BROS_MATRIX);
|
||||
matrix->SetScrollbar(this->vscrollMatrix);
|
||||
matrix->SetCount(_roadstop_gui_settings.roadstop_count);
|
||||
matrix->SetClicked(_roadstop_gui_settings.roadstop_type);
|
||||
|
||||
this->EnsureSelectedClassIsVisible();
|
||||
this->CheckOrientationValid();
|
||||
}
|
||||
}
|
||||
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
{
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
this->PickerWindowBase::Close();
|
||||
}
|
||||
|
||||
/** Sort classes by RoadStopClassID. */
|
||||
static bool RoadStopClassIDSorter(RoadStopClassID const &a, RoadStopClassID const &b)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
/** Filter classes by class name. */
|
||||
static bool TagNameFilter(RoadStopClassID const *sc, StringFilter &filter)
|
||||
{
|
||||
filter.ResetState();
|
||||
filter.AddLine(GetString(RoadStopClass::Get(*sc)->name));
|
||||
return filter.GetState();
|
||||
}
|
||||
|
||||
inline bool ShowNewStops() const
|
||||
{
|
||||
return this->vscrollList != nullptr;
|
||||
}
|
||||
|
||||
void BuildRoadStopClassesAvailable()
|
||||
{
|
||||
if (!this->roadstop_classes.NeedRebuild()) return;
|
||||
|
||||
this->roadstop_classes.clear();
|
||||
this->roadstop_classes.reserve(RoadStopClass::GetClassCount());
|
||||
|
||||
for (const auto &cls : RoadStopClass::Classes()) {
|
||||
/* Skip waypoints. */
|
||||
if (IsWaypointClass(cls)) continue;
|
||||
if (GetIfClassHasNewStopsByType(&cls, this->roadStopType, _cur_roadtype)) this->roadstop_classes.push_back(cls.Index());
|
||||
}
|
||||
|
||||
if (this->ShowNewStops()) {
|
||||
this->roadstop_classes.Filter(this->string_filter);
|
||||
this->roadstop_classes.RebuildDone();
|
||||
this->roadstop_classes.Sort();
|
||||
|
||||
this->vscrollList->SetCount(this->roadstop_classes.size());
|
||||
}
|
||||
}
|
||||
|
||||
void SelectFirstAvailableTypeIfUnavailable()
|
||||
{
|
||||
const RoadStopClass *rs_class = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class);
|
||||
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
|
||||
|
||||
if (IsRoadStopAvailable(rs_class->GetSpec(_roadstop_gui_settings.roadstop_type), st)) return;
|
||||
for (uint i = 0; i < _roadstop_gui_settings.roadstop_count; i++) {
|
||||
if (IsRoadStopAvailable(rs_class->GetSpec(i), st)) {
|
||||
_roadstop_gui_settings.roadstop_type = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->PickerWindow::Close();
|
||||
}
|
||||
|
||||
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
|
||||
{
|
||||
if (!gui_scope) return;
|
||||
this->PickerWindow::OnInvalidateData(data, gui_scope);
|
||||
|
||||
this->BuildRoadStopClassesAvailable();
|
||||
}
|
||||
|
||||
EventState OnHotkey(int hotkey) override
|
||||
{
|
||||
if (hotkey == BROSHK_FOCUS_FILTER_BOX) {
|
||||
this->SetFocusedWidget(WID_BROS_FILTER_EDITBOX);
|
||||
SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
return ES_NOT_HANDLED;
|
||||
}
|
||||
|
||||
void OnEditboxChanged(WidgetID widget) override
|
||||
{
|
||||
if (widget == WID_BROS_FILTER_EDITBOX) {
|
||||
string_filter.SetFilterTerm(this->filter_editbox.text.buf);
|
||||
this->roadstop_classes.SetFilterState(!string_filter.IsEmpty());
|
||||
this->roadstop_classes.ForceRebuild();
|
||||
this->InvalidateData();
|
||||
if (gui_scope) {
|
||||
this->CheckOrientationValid();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1353,52 +1272,23 @@ public:
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BROS_NEWST_LIST: {
|
||||
Dimension d = { 0, 0 };
|
||||
for (auto rs_class : this->roadstop_classes) {
|
||||
d = maxdim(d, GetStringBoundingBox(RoadStopClass::Get(rs_class)->name));
|
||||
}
|
||||
size.width = std::max(size.width, d.width + padding.width);
|
||||
this->line_height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.matrix.Vertical();
|
||||
size.height = 5 * this->line_height;
|
||||
resize.height = this->line_height;
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_SHOW_NEWST_TYPE: {
|
||||
Dimension d = {0, 0};
|
||||
StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
|
||||
for (auto roadstop_class : this->roadstop_classes) {
|
||||
RoadStopClass *rs_class = RoadStopClass::Get(roadstop_class);
|
||||
for (uint j = 0; j < rs_class->GetSpecCount(); j++) {
|
||||
const RoadStopSpec *roadstopspec = rs_class->GetSpec(j);
|
||||
SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT_ROADSTOP);
|
||||
d = maxdim(d, GetStringBoundingBox(str));
|
||||
}
|
||||
}
|
||||
size.width = std::max(size.width, d.width + padding.width);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_STATION_NE:
|
||||
case WID_BROS_STATION_SE:
|
||||
case WID_BROS_STATION_SW:
|
||||
case WID_BROS_STATION_NW:
|
||||
case WID_BROS_STATION_X:
|
||||
case WID_BROS_STATION_Y:
|
||||
case WID_BROS_IMAGE:
|
||||
size.width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size.height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
break;
|
||||
|
||||
case WID_BROS_MATRIX:
|
||||
fill.height = 1;
|
||||
resize.height = 1;
|
||||
size.width = ScaleGUITrad(PREVIEW_WIDTH) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size.height = ScaleGUITrad(PREVIEW_HEIGHT) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
break;
|
||||
|
||||
case WID_BROS_ACCEPTANCE:
|
||||
size.height = this->coverage_height;
|
||||
break;
|
||||
|
||||
default:
|
||||
this->PickerWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1424,13 +1314,13 @@ public:
|
||||
case WID_BROS_STATION_X:
|
||||
case WID_BROS_STATION_Y: {
|
||||
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
|
||||
DrawPixelInfo tmp_dpi;
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
|
||||
if (FillDrawPixelInfo(&tmp_dpi, ir)) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
|
||||
int y = (ir.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
|
||||
int x = (ir.Width() - ScaleSpriteTrad(PREVIEW_WIDTH)) / 2 + ScaleSpriteTrad(PREVIEW_LEFT);
|
||||
int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM);
|
||||
if (spec == nullptr) {
|
||||
StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE);
|
||||
} else {
|
||||
@ -1440,63 +1330,9 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_NEWST_LIST: {
|
||||
uint statclass = 0;
|
||||
uint row = 0;
|
||||
for (auto rs_class : this->roadstop_classes) {
|
||||
if (this->vscrollList->IsVisible(statclass)) {
|
||||
DrawString(r.left + WidgetDimensions::scaled.matrix.left, r.right, row * this->line_height + r.top + WidgetDimensions::scaled.matrix.top,
|
||||
RoadStopClass::Get(rs_class)->name,
|
||||
rs_class == _roadstop_gui_settings.roadstop_class ? TC_WHITE : TC_BLACK);
|
||||
row++;
|
||||
}
|
||||
statclass++;
|
||||
}
|
||||
default:
|
||||
this->PickerWindow::DrawWidget(r, widget);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_IMAGE: {
|
||||
uint16_t type = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
|
||||
assert(type < _roadstop_gui_settings.roadstop_count);
|
||||
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(type);
|
||||
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
|
||||
|
||||
/* Set up a clipping area for the sprite preview. */
|
||||
DrawPixelInfo tmp_dpi;
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
|
||||
if (FillDrawPixelInfo(&tmp_dpi, ir)) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (ir.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
|
||||
int y = (ir.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
|
||||
if (spec == nullptr) {
|
||||
StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui_settings.orientation);
|
||||
} else {
|
||||
DiagDirection orientation = _roadstop_gui_settings.orientation;
|
||||
if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END;
|
||||
DrawRoadStopTile(x, y, _cur_roadtype, spec, st, (uint8_t)orientation);
|
||||
}
|
||||
}
|
||||
if (!IsRoadStopAvailable(spec, st)) {
|
||||
GfxFillRect(ir, PC_BLACK, FILLRECT_CHECKER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
if (this->vscrollList != nullptr) {
|
||||
this->vscrollList->SetCapacityFromWidget(this, WID_BROS_NEWST_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
void SetStringParameters(WidgetID widget) const override
|
||||
{
|
||||
if (widget == WID_BROS_SHOW_NEWST_TYPE) {
|
||||
const RoadStopSpec *roadstopspec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT_ROADSTOP);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1510,12 +1346,12 @@ public:
|
||||
case WID_BROS_STATION_X:
|
||||
case WID_BROS_STATION_Y:
|
||||
if (widget < WID_BROS_STATION_X) {
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
|
||||
if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) return;
|
||||
}
|
||||
this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
_roadstop_gui_settings.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE);
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui_settings.orientation);
|
||||
this->RaiseWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
_roadstop_gui.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE);
|
||||
this->LowerWidget(WID_BROS_STATION_NE + _roadstop_gui.orientation);
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
@ -1531,49 +1367,8 @@ public:
|
||||
SetViewportCatchmentStation(nullptr, true);
|
||||
break;
|
||||
|
||||
case WID_BROS_NEWST_LIST: {
|
||||
auto it = this->vscrollList->GetScrolledItemFromWidget(this->roadstop_classes, pt.y, this, WID_BROS_NEWST_LIST);
|
||||
if (it == this->roadstop_classes.end()) return;
|
||||
RoadStopClassID class_id = *it;
|
||||
if (_roadstop_gui_settings.roadstop_class != class_id && GetIfClassHasNewStopsByType(RoadStopClass::Get(class_id), roadStopType, _cur_roadtype)) {
|
||||
_roadstop_gui_settings.roadstop_class = class_id;
|
||||
RoadStopClass *rsclass = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class);
|
||||
_roadstop_gui_settings.roadstop_count = rsclass->GetSpecCount();
|
||||
_roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, std::max(0, (int)_roadstop_gui_settings.roadstop_count - 1));
|
||||
this->SelectFirstAvailableTypeIfUnavailable();
|
||||
|
||||
NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BROS_MATRIX);
|
||||
matrix->SetCount(_roadstop_gui_settings.roadstop_count);
|
||||
matrix->SetClicked(_roadstop_gui_settings.roadstop_type);
|
||||
}
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
this->CheckOrientationValid();
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_IMAGE: {
|
||||
uint16_t y = this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->GetCurrentElement();
|
||||
if (y >= _roadstop_gui_settings.roadstop_count) return;
|
||||
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(y);
|
||||
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
|
||||
|
||||
if (!IsRoadStopAvailable(spec, st)) return;
|
||||
|
||||
_roadstop_gui_settings.roadstop_type = y;
|
||||
|
||||
this->GetWidget<NWidgetBase>(widget)->GetParentWidget<NWidgetMatrix>()->SetClicked(_roadstop_gui_settings.roadstop_type);
|
||||
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
this->CheckOrientationValid();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
this->PickerWindow::OnClick(pt, widget, click_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1583,119 +1378,66 @@ public:
|
||||
CheckRedrawStationCoverage(this);
|
||||
}
|
||||
|
||||
IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) {
|
||||
this->InvalidateData();
|
||||
}};
|
||||
|
||||
static inline HotkeyList road_hotkeys{"buildroadstop", {
|
||||
Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX),
|
||||
Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX),
|
||||
}};
|
||||
|
||||
static inline HotkeyList tram_hotkeys{"buildtramstop", {
|
||||
Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX),
|
||||
Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX),
|
||||
}};
|
||||
};
|
||||
|
||||
Listing BuildRoadStationWindow::last_sorting = { false, 0 };
|
||||
Filtering BuildRoadStationWindow::last_filtering = { false, 0 };
|
||||
|
||||
const std::initializer_list<BuildRoadStationWindow::GUIRoadStopClassList::SortFunction * const> BuildRoadStationWindow::sorter_funcs = {
|
||||
&RoadStopClassIDSorter,
|
||||
};
|
||||
|
||||
const std::initializer_list<BuildRoadStationWindow::GUIRoadStopClassList::FilterFunction * const> BuildRoadStationWindow::filter_funcs = {
|
||||
&TagNameFilter,
|
||||
};
|
||||
|
||||
/** Widget definition of the build road station window */
|
||||
static constexpr NWidgetPart _nested_road_station_picker_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidgetFunction(MakePickerClassWidgets),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_AVAILABLE_ORIENTATIONS),
|
||||
/* 6-orientation plane. */
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0),
|
||||
SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_AVAILABLE_ORIENTATIONS),
|
||||
/* 6-orientation plane. */
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
/* 2-orientation plane. */
|
||||
NWidget(NWID_VERTICAL), SetPIPRatio(0, 0, 1),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX),
|
||||
/* Hidden panel as NWID_MATRIX does not support SetScrollbar() */
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetPIP(0, 2, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE),
|
||||
SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
EndContainer(),
|
||||
/* 2-orientation plane. */
|
||||
NWidget(NWID_VERTICAL), SetPIPRatio(0, 0, 1),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidgetFunction(MakePickerTypeWidgets),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@ -1713,67 +1455,29 @@ static constexpr NWidgetPart _nested_tram_station_picker_widgets[] = {
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0),
|
||||
SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidgetFunction(MakePickerClassWidgets),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), SetPadding(WidgetDimensions::unscaled.picker),
|
||||
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetFill(0, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetFill(0, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX),
|
||||
/* Hidden panel as NWID_MATRIX does not support SetScrollbar() */
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetPIP(0, 2, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE),
|
||||
SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidgetFunction(MakePickerTypeWidgets),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@ -1793,7 +1497,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs)
|
||||
void InitializeRoadGui()
|
||||
{
|
||||
_road_depot_orientation = DIAGDIR_NW;
|
||||
_roadstop_gui_settings.orientation = DIAGDIR_NW;
|
||||
_roadstop_gui.orientation = DIAGDIR_NW;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,12 +21,13 @@
|
||||
* Draw a waypoint
|
||||
* @param x coordinate
|
||||
* @param y coordinate
|
||||
* @param stat_id station id
|
||||
* @param station_class Station class.
|
||||
* @param station_type Station type within class.
|
||||
* @param railtype RailType to use for
|
||||
*/
|
||||
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype)
|
||||
void DrawWaypointSprite(int x, int y, StationClassID station_class, uint16_t station_type, RailType railtype)
|
||||
{
|
||||
if (!DrawStationTile(x, y, railtype, AXIS_X, STAT_CLASS_WAYP, stat_id)) {
|
||||
if (!DrawStationTile(x, y, railtype, AXIS_X, station_class, station_type)) {
|
||||
StationPickerDrawSprite(x, y, STATION_WAYPOINT, railtype, INVALID_ROADTYPE, AXIS_X);
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,12 @@
|
||||
#include "command_type.h"
|
||||
#include "station_type.h"
|
||||
|
||||
enum StationClassID : uint16_t;
|
||||
|
||||
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags);
|
||||
|
||||
Axis GetAxisForNewWaypoint(TileIndex tile);
|
||||
void ShowWaypointWindow(const Waypoint *wp);
|
||||
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype);
|
||||
void DrawWaypointSprite(int x, int y, StationClassID station_class, uint16_t station_type, RailType railtype);
|
||||
|
||||
#endif /* WAYPOINT_FUNC_H */
|
||||
|
@ -39,6 +39,7 @@ add_files(
|
||||
object_widget.h
|
||||
order_widget.h
|
||||
osk_widget.h
|
||||
picker_widget.h
|
||||
rail_widget.h
|
||||
road_widget.h
|
||||
screenshot_widget.h
|
||||
|
@ -12,18 +12,10 @@
|
||||
|
||||
/** Widgets of the #BuildObjectWindow class. */
|
||||
enum BuildObjectWidgets : WidgetID {
|
||||
WID_BO_FILTER, ///< The filter text box for the object list.
|
||||
WID_BO_CLASS_LIST, ///< The list with classes.
|
||||
WID_BO_SCROLLBAR, ///< The scrollbar associated with the list.
|
||||
WID_BO_OBJECT_MATRIX, ///< The matrix with preview sprites.
|
||||
WID_BO_OBJECT_SPRITE, ///< A preview sprite of the object.
|
||||
WID_BO_OBJECT_NAME, ///< The name of the selected object.
|
||||
WID_BO_OBJECT_SIZE, ///< The size of the selected object.
|
||||
WID_BO_INFO, ///< Other information about the object (from the NewGRF).
|
||||
|
||||
WID_BO_SELECT_MATRIX, ///< Selection preview matrix of objects of a given class.
|
||||
WID_BO_SELECT_IMAGE, ///< Preview image in the #WID_BO_SELECT_MATRIX.
|
||||
WID_BO_SELECT_SCROLL, ///< Scrollbar next to the #WID_BO_SELECT_MATRIX.
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_OBJECT_WIDGET_H */
|
||||
|
31
src/widgets/picker_widget.h
Normal file
31
src/widgets/picker_widget.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file picker_widget.h Types related to the picker widgets. */
|
||||
|
||||
#ifndef WIDGETS_PICKER_WIDGET_H
|
||||
#define WIDGETS_PICKER_WIDGET_H
|
||||
|
||||
/** Widgets of the #PickerWindow class. */
|
||||
enum PickerClassWindowWidgets : WidgetID {
|
||||
WID_PW_START = 1 << 16, ///< Dummy to ensure widgets don't overlap.
|
||||
|
||||
WID_PW_CLASS_SEL, ///< Stack to hide the class picker.
|
||||
WID_PW_CLASS_FILTER, ///< Editbox filter.
|
||||
WID_PW_CLASS_LIST, ///< List of classes.
|
||||
WID_PW_CLASS_SCROLL, ///< Scrollbar for list of classes.
|
||||
|
||||
WID_PW_TYPE_SEL, ///< Stack to hide the type picker.
|
||||
WID_PW_TYPE_FILTER, ///< Text filter.
|
||||
WID_PW_TYPE_MATRIX, ///< Matrix with items.
|
||||
WID_PW_TYPE_ITEM, ///< A single item.
|
||||
WID_PW_TYPE_SCROLL, ///< Scrollbar for the matrix.
|
||||
WID_PW_TYPE_NAME, ///< Name of selected item.
|
||||
WID_PW_TYPE_RESIZE, ///< Type resize handle.
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_PICKER_WIDGET_H */
|
@ -60,20 +60,6 @@ enum BuildRailStationWidgets : WidgetID {
|
||||
WID_BRAS_HIGHLIGHT_ON, ///< Button for turning coverage highlighting on.
|
||||
WID_BRAS_COVERAGE_TEXTS, ///< Empty space for the coverage texts.
|
||||
|
||||
WID_BRAS_MATRIX, ///< Matrix widget displaying the available stations.
|
||||
WID_BRAS_IMAGE, ///< Panel used at each cell of the matrix.
|
||||
WID_BRAS_MATRIX_SCROLL, ///< Scrollbar of the matrix widget.
|
||||
|
||||
WID_BRAS_FILTER_CONTAINER, ///< Container for the filter text box for the station class list.
|
||||
WID_BRAS_FILTER_EDITBOX, ///< Filter text box for the station class list.
|
||||
WID_BRAS_SHOW_NEWST_DEFSIZE, ///< Selection for default-size button for newstation.
|
||||
WID_BRAS_SHOW_NEWST_ADDITIONS, ///< Selection for newstation class selection list.
|
||||
WID_BRAS_SHOW_NEWST_MATRIX, ///< Selection for newstation image matrix.
|
||||
WID_BRAS_SHOW_NEWST_RESIZE, ///< Selection for panel and resize at bottom right for newstation.
|
||||
WID_BRAS_SHOW_NEWST_TYPE, ///< Display of selected station type.
|
||||
WID_BRAS_NEWST_LIST, ///< List with available newstation classes.
|
||||
WID_BRAS_NEWST_SCROLL, ///< Scrollbar of the #WID_BRAS_NEWST_LIST.
|
||||
|
||||
WID_BRAS_PLATFORM_NUM_BEGIN = WID_BRAS_PLATFORM_NUM_1 - 1, ///< Helper for determining the chosen platform width.
|
||||
WID_BRAS_PLATFORM_LEN_BEGIN = WID_BRAS_PLATFORM_LEN_1 - 1, ///< Helper for determining the chosen platform length.
|
||||
};
|
||||
|
@ -53,21 +53,7 @@ enum BuildRoadStationWidgets : WidgetID {
|
||||
WID_BROS_LT_OFF, ///< Turn off area highlight.
|
||||
WID_BROS_LT_ON, ///< Turn on area highlight.
|
||||
WID_BROS_ACCEPTANCE, ///< Station acceptance info.
|
||||
WID_BROS_MATRIX, ///< Matrix widget displaying all available road stops.
|
||||
WID_BROS_IMAGE, ///< Panel used for each image of the matrix.
|
||||
WID_BROS_MATRIX_SCROLL, ///< Scrollbar of the #WID_BROS_SHOW_NEWST_ADDITIONS.
|
||||
WID_BROS_FILTER_CONTAINER, ///< Container for the filter text box for the road stop class list.
|
||||
WID_BROS_FILTER_EDITBOX, ///< Filter text box for the road stop class list.
|
||||
WID_BROS_AVAILABLE_ORIENTATIONS, ///< Selection for selecting 6 or 2 orientations.
|
||||
WID_BROS_SHOW_NEWST_DEFSIZE, ///< Selection for default-size button for new road stops.
|
||||
WID_BROS_SHOW_NEWST_ADDITIONS, ///< Selection for new class selection list.
|
||||
WID_BROS_SHOW_NEWST_MATRIX, ///< Selection for new stop image matrix.
|
||||
WID_BROS_SHOW_NEWST_RESIZE, ///< Selection for panel and resize at bottom right for new stops.
|
||||
WID_BROS_SHOW_NEWST_ORIENTATION, ///< Selection for the orientation string for new stops.
|
||||
WID_BROS_SHOW_NEWST_TYPE_SEL, ///< Selection for the type name.
|
||||
WID_BROS_SHOW_NEWST_TYPE, ///< Display of selected stop type.
|
||||
WID_BROS_NEWST_LIST, ///< List with new road stops.
|
||||
WID_BROS_NEWST_SCROLL, ///< Scrollbar of the #WID_BROS_NEWST_LIST.
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_ROAD_WIDGET_H */
|
||||
|
Loading…
Reference in New Issue
Block a user