Codechange: Move dropdown and slider out of widgets directory. (#12403)
Also shuffle headers to place widget includes near end. This leaves the widgets directory solely for defining Widget IDs.
This commit is contained in:
@@ -11,9 +11,6 @@ add_files(
|
||||
date_widget.h
|
||||
depot_widget.h
|
||||
dock_widget.h
|
||||
dropdown.cpp
|
||||
dropdown_func.h
|
||||
dropdown_type.h
|
||||
dropdown_widget.h
|
||||
engine_widget.h
|
||||
error_widget.h
|
||||
@@ -48,8 +45,6 @@ add_files(
|
||||
script_widget.h
|
||||
settings_widget.h
|
||||
sign_widget.h
|
||||
slider.cpp
|
||||
slider_func.h
|
||||
smallmap_widget.h
|
||||
station_widget.h
|
||||
statusbar_widget.h
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
/*
|
||||
* 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 dropdown.cpp Implementation of the dropdown widget. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../window_gui.h"
|
||||
#include "../string_func.h"
|
||||
#include "../strings_func.h"
|
||||
#include "../window_func.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "../timer/timer.h"
|
||||
#include "../timer/timer_window.h"
|
||||
#include "dropdown_type.h"
|
||||
|
||||
#include "dropdown_widget.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
|
||||
static constexpr NWidgetPart _nested_dropdown_menu_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_END, WID_DM_ITEMS), SetScrollbar(WID_DM_SCROLL), EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_DM_SHOW_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_END, WID_DM_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
static WindowDesc _dropdown_desc(
|
||||
WDP_MANUAL, nullptr, 0, 0,
|
||||
WC_DROPDOWN_MENU, WC_NONE,
|
||||
WDF_NO_FOCUS,
|
||||
std::begin(_nested_dropdown_menu_widgets), std::end(_nested_dropdown_menu_widgets)
|
||||
);
|
||||
|
||||
/** Drop-down menu window */
|
||||
struct DropdownWindow : Window {
|
||||
WidgetID parent_button; ///< Parent widget number where the window is dropped from.
|
||||
Rect wi_rect; ///< Rect of the button that opened the dropdown.
|
||||
DropDownList list; ///< List with dropdown menu items.
|
||||
int selected_result; ///< Result value of the selected item in the list.
|
||||
uint8_t click_delay = 0; ///< Timer to delay selection.
|
||||
bool drag_mode = true;
|
||||
bool instant_close; ///< Close the window when the mouse button is raised.
|
||||
bool persist; ///< Persist dropdown menu.
|
||||
int scrolling = 0; ///< If non-zero, auto-scroll the item list (one time).
|
||||
Point position; ///< Position of the topleft corner of the window.
|
||||
Scrollbar *vscroll;
|
||||
|
||||
Dimension items_dim; ///< Calculated cropped and padded dimension for the items widget.
|
||||
|
||||
/**
|
||||
* Create a dropdown menu.
|
||||
* @param parent Parent window.
|
||||
* @param list Dropdown item list.
|
||||
* @param selected Initial selected result of the list.
|
||||
* @param button Widget of the parent window doing the dropdown.
|
||||
* @param wi_rect Rect of the button that opened the dropdown.
|
||||
* @param instant_close Close the window when the mouse button is raised.
|
||||
* @param wi_colour Colour of the parent widget.
|
||||
* @param persist Dropdown menu will persist.
|
||||
*/
|
||||
DropdownWindow(Window *parent, DropDownList &&list, int selected, WidgetID button, const Rect wi_rect, bool instant_close, Colours wi_colour, bool persist)
|
||||
: Window(&_dropdown_desc)
|
||||
, parent_button(button)
|
||||
, wi_rect(wi_rect)
|
||||
, list(std::move(list))
|
||||
, selected_result(selected)
|
||||
, instant_close(instant_close)
|
||||
, persist(persist)
|
||||
{
|
||||
assert(!this->list.empty());
|
||||
|
||||
this->parent = parent;
|
||||
|
||||
this->CreateNestedTree();
|
||||
|
||||
this->GetWidget<NWidgetCore>(WID_DM_ITEMS)->colour = wi_colour;
|
||||
this->GetWidget<NWidgetCore>(WID_DM_SCROLL)->colour = wi_colour;
|
||||
this->vscroll = this->GetScrollbar(WID_DM_SCROLL);
|
||||
this->UpdateSizeAndPosition();
|
||||
|
||||
this->FinishInitNested(0);
|
||||
CLRBITS(this->flags, WF_WHITE_BORDER);
|
||||
}
|
||||
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
{
|
||||
/* Finish closing the dropdown, so it doesn't affect new window placement.
|
||||
* Also mark it dirty in case the callback deals with the screen. (e.g. screenshots). */
|
||||
this->Window::Close();
|
||||
|
||||
Point pt = _cursor.pos;
|
||||
pt.x -= this->parent->left;
|
||||
pt.y -= this->parent->top;
|
||||
this->parent->OnDropdownClose(pt, this->parent_button, this->selected_result, this->instant_close);
|
||||
|
||||
/* Set flag on parent widget to indicate that we have just closed. */
|
||||
NWidgetCore *nwc = this->parent->GetWidget<NWidgetCore>(this->parent_button);
|
||||
if (nwc != nullptr) SetBit(nwc->disp_flags, NDB_DROPDOWN_CLOSED);
|
||||
}
|
||||
|
||||
void OnFocusLost(bool closing) override
|
||||
{
|
||||
if (!closing) {
|
||||
this->instant_close = false;
|
||||
this->Close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fit dropdown list into available height, rounding to average item size. Width is adjusted if scrollbar is present.
|
||||
* @param[in,out] desired Desired dimensions of dropdown list.
|
||||
* @param list Dimensions of the list itself, without padding or cropping.
|
||||
* @param available_height Available height to fit list within.
|
||||
*/
|
||||
void FitAvailableHeight(Dimension &desired, const Dimension &list, uint available_height)
|
||||
{
|
||||
if (desired.height < available_height) return;
|
||||
|
||||
/* If the dropdown doesn't fully fit, we a need a dropdown. */
|
||||
uint avg_height = list.height / (uint)this->list.size();
|
||||
uint rows = std::max((available_height - WidgetDimensions::scaled.dropdownlist.Vertical()) / avg_height, 1U);
|
||||
|
||||
desired.width = std::max(list.width, desired.width - NWidgetScrollbar::GetVerticalDimension().width);
|
||||
desired.height = rows * avg_height + WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update size and position of window to fit dropdown list into available space.
|
||||
*/
|
||||
void UpdateSizeAndPosition()
|
||||
{
|
||||
Rect button_rect = this->wi_rect.Translate(this->parent->left, this->parent->top);
|
||||
|
||||
/* Get the dimensions required for the list. */
|
||||
Dimension list_dim = GetDropDownListDimension(this->list);
|
||||
|
||||
/* Set up dimensions for the items widget. */
|
||||
Dimension widget_dim = list_dim;
|
||||
widget_dim.width += WidgetDimensions::scaled.dropdownlist.Horizontal();
|
||||
widget_dim.height += WidgetDimensions::scaled.dropdownlist.Vertical();
|
||||
|
||||
/* Width should match at least the width of the parent widget. */
|
||||
widget_dim.width = std::max<uint>(widget_dim.width, button_rect.Width());
|
||||
|
||||
/* Available height below (or above, if the dropdown is placed above the widget). */
|
||||
uint available_height_below = std::max(GetMainViewBottom() - button_rect.bottom - 1, 0);
|
||||
uint available_height_above = std::max(button_rect.top - 1 - GetMainViewTop(), 0);
|
||||
|
||||
/* Is it better to place the dropdown above the widget? */
|
||||
if (widget_dim.height > available_height_below && available_height_above > available_height_below) {
|
||||
FitAvailableHeight(widget_dim, list_dim, available_height_above);
|
||||
this->position.y = button_rect.top - widget_dim.height;
|
||||
} else {
|
||||
FitAvailableHeight(widget_dim, list_dim, available_height_below);
|
||||
this->position.y = button_rect.bottom + 1;
|
||||
}
|
||||
|
||||
this->position.x = (_current_text_dir == TD_RTL) ? button_rect.right + 1 - (int)widget_dim.width : button_rect.left;
|
||||
|
||||
this->items_dim = widget_dim;
|
||||
this->GetWidget<NWidgetStacked>(WID_DM_SHOW_SCROLL)->SetDisplayedPlane(list_dim.height > widget_dim.height ? 0 : SZSP_NONE);
|
||||
|
||||
/* Capacity is the average number of items visible */
|
||||
this->vscroll->SetCapacity((widget_dim.height - WidgetDimensions::scaled.dropdownlist.Vertical()) * this->list.size() / list_dim.height);
|
||||
this->vscroll->SetCount(this->list.size());
|
||||
|
||||
/* If the dropdown is positioned above the parent widget, start selection at the bottom. */
|
||||
if (this->position.y < button_rect.top && list_dim.height > widget_dim.height) this->vscroll->UpdatePosition(INT_MAX);
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
|
||||
{
|
||||
if (widget == WID_DM_ITEMS) *size = this->items_dim;
|
||||
}
|
||||
|
||||
Point OnInitialPosition([[maybe_unused]] int16_t sm_width, [[maybe_unused]] int16_t sm_height, [[maybe_unused]] int window_number) override
|
||||
{
|
||||
return this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the dropdown item under the cursor.
|
||||
* @param[out] value Selected item, if function returns \c true.
|
||||
* @return Cursor points to a dropdown item.
|
||||
*/
|
||||
bool GetDropDownItem(int &value)
|
||||
{
|
||||
if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false;
|
||||
|
||||
const Rect &r = this->GetWidget<NWidgetBase>(WID_DM_ITEMS)->GetCurrentRect().Shrink(WidgetDimensions::scaled.dropdownlist);
|
||||
int y = _cursor.pos.y - this->top - r.top;
|
||||
int pos = this->vscroll->GetPosition();
|
||||
|
||||
for (const auto &item : this->list) {
|
||||
/* Skip items that are scrolled up */
|
||||
if (--pos >= 0) continue;
|
||||
|
||||
int item_height = item->Height();
|
||||
|
||||
if (y < item_height) {
|
||||
if (item->masked || !item->Selectable()) return false;
|
||||
value = item->result;
|
||||
return true;
|
||||
}
|
||||
|
||||
y -= item_height;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrawWidget(const Rect &r, WidgetID widget) const override
|
||||
{
|
||||
if (widget != WID_DM_ITEMS) return;
|
||||
|
||||
Colours colour = this->GetWidget<NWidgetCore>(widget)->colour;
|
||||
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.dropdownlist);
|
||||
int y = ir.top;
|
||||
int pos = this->vscroll->GetPosition();
|
||||
for (const auto &item : this->list) {
|
||||
int item_height = item->Height();
|
||||
|
||||
/* Skip items that are scrolled up */
|
||||
if (--pos >= 0) continue;
|
||||
|
||||
if (y + item_height - 1 <= ir.bottom) {
|
||||
Rect full{ir.left, y, ir.right, y + item_height - 1};
|
||||
|
||||
bool selected = (this->selected_result == item->result) && item->Selectable();
|
||||
if (selected) GfxFillRect(full, PC_BLACK);
|
||||
|
||||
item->Draw(full, full.Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero), selected, colour);
|
||||
}
|
||||
y += item_height;
|
||||
}
|
||||
}
|
||||
|
||||
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
|
||||
{
|
||||
if (widget != WID_DM_ITEMS) return;
|
||||
int item;
|
||||
if (this->GetDropDownItem(item)) {
|
||||
this->click_delay = 4;
|
||||
this->selected_result = item;
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Rate limit how fast scrolling happens. */
|
||||
IntervalTimer<TimerWindow> scroll_interval = {std::chrono::milliseconds(30), [this](auto) {
|
||||
if (this->scrolling == 0) return;
|
||||
|
||||
if (this->vscroll->UpdatePosition(this->scrolling)) this->SetDirty();
|
||||
|
||||
this->scrolling = 0;
|
||||
}};
|
||||
|
||||
void OnMouseLoop() override
|
||||
{
|
||||
if (this->click_delay != 0 && --this->click_delay == 0) {
|
||||
/* Close the dropdown, so it doesn't affect new window placement.
|
||||
* Also mark it dirty in case the callback deals with the screen. (e.g. screenshots). */
|
||||
if (!this->persist) this->Close();
|
||||
this->parent->OnDropdownSelect(this->parent_button, this->selected_result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->drag_mode) {
|
||||
int item;
|
||||
|
||||
if (!_left_button_clicked) {
|
||||
this->drag_mode = false;
|
||||
if (!this->GetDropDownItem(item)) {
|
||||
if (this->instant_close) this->Close();
|
||||
return;
|
||||
}
|
||||
this->click_delay = 2;
|
||||
} else {
|
||||
if (_cursor.pos.y <= this->top + 2) {
|
||||
/* Cursor is above the list, set scroll up */
|
||||
this->scrolling = -1;
|
||||
return;
|
||||
} else if (_cursor.pos.y >= this->top + this->height - 2) {
|
||||
/* Cursor is below list, set scroll down */
|
||||
this->scrolling = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->GetDropDownItem(item)) return;
|
||||
}
|
||||
|
||||
if (this->selected_result != item) {
|
||||
this->selected_result = item;
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceList(DropDownList &&list)
|
||||
{
|
||||
this->list = std::move(list);
|
||||
this->UpdateSizeAndPosition();
|
||||
this->ReInit(0, 0);
|
||||
this->InitializePositionSize(this->position.x, this->position.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
|
||||
this->SetDirty();
|
||||
}
|
||||
};
|
||||
|
||||
void ReplaceDropDownList(Window *parent, DropDownList &&list)
|
||||
{
|
||||
DropdownWindow *ddw = dynamic_cast<DropdownWindow *>(parent->FindChildWindow(WC_DROPDOWN_MENU));
|
||||
if (ddw != nullptr) ddw->ReplaceList(std::move(list));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine width and height required to fully display a DropDownList
|
||||
* @param list The list.
|
||||
* @return Dimension required to display the list.
|
||||
*/
|
||||
Dimension GetDropDownListDimension(const DropDownList &list)
|
||||
{
|
||||
Dimension dim{};
|
||||
for (const auto &item : list) {
|
||||
dim.height += item->Height();
|
||||
dim.width = std::max(dim.width, item->Width());
|
||||
}
|
||||
dim.width += WidgetDimensions::scaled.dropdowntext.Horizontal();
|
||||
return dim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a drop down list.
|
||||
* @param w Parent window for the list.
|
||||
* @param list Prepopulated DropDownList.
|
||||
* @param selected The initially selected list item.
|
||||
* @param button The widget which is passed to Window::OnDropdownSelect and OnDropdownClose.
|
||||
* Unless you override those functions, this should be then widget index of the dropdown button.
|
||||
* @param wi_rect Coord of the parent drop down button, used to position the dropdown menu.
|
||||
* @param instant_close Set to true if releasing mouse button should close the
|
||||
* list regardless of where the cursor is.
|
||||
* @param persist Set if this dropdown should stay open after an option is selected.
|
||||
*/
|
||||
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, bool instant_close, bool persist)
|
||||
{
|
||||
CloseWindowByClass(WC_DROPDOWN_MENU);
|
||||
new DropdownWindow(w, std::move(list), selected, button, wi_rect, instant_close, wi_colour, persist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a drop down list.
|
||||
* @param w Parent window for the list.
|
||||
* @param list Prepopulated DropDownList.
|
||||
* @param selected The initially selected list item.
|
||||
* @param button The widget within the parent window that is used to determine
|
||||
* the list's location.
|
||||
* @param width Override the minimum width determined by the selected widget and list contents.
|
||||
* @param instant_close Set to true if releasing mouse button should close the
|
||||
* list regardless of where the cursor is.
|
||||
* @param persist Set if this dropdown should stay open after an option is selected.
|
||||
*/
|
||||
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
|
||||
{
|
||||
/* Our parent's button widget is used to determine where to place the drop
|
||||
* down list window. */
|
||||
NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
|
||||
Rect wi_rect = nwi->GetCurrentRect();
|
||||
Colours wi_colour = nwi->colour;
|
||||
|
||||
if ((nwi->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
|
||||
nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
|
||||
} else {
|
||||
nwi->SetLowered(true);
|
||||
}
|
||||
nwi->SetDirty(w);
|
||||
|
||||
if (width != 0) {
|
||||
if (_current_text_dir == TD_RTL) {
|
||||
wi_rect.left = wi_rect.right + 1 - ScaleGUITrad(width);
|
||||
} else {
|
||||
wi_rect.right = wi_rect.left + ScaleGUITrad(width) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
ShowDropDownListAt(w, std::move(list), selected, button, wi_rect, wi_colour, instant_close, persist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dropdown menu window near a widget of the parent window.
|
||||
* The result code of the items is their index in the \a strings list.
|
||||
* @param w Parent window that wants the dropdown menu.
|
||||
* @param strings Menu list, end with #INVALID_STRING_ID
|
||||
* @param selected Index of initial selected item.
|
||||
* @param button Button widget number of the parent window \a w that wants the dropdown menu.
|
||||
* @param disabled_mask Bitmask for disabled items (items with their bit set are displayed, but not selectable in the dropdown list).
|
||||
* @param hidden_mask Bitmask for hidden items (items with their bit set are not copied to the dropdown list).
|
||||
* @param width Minimum width of the dropdown menu.
|
||||
*/
|
||||
void ShowDropDownMenu(Window *w, const StringID *strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
|
||||
{
|
||||
DropDownList list;
|
||||
|
||||
for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
|
||||
if (!HasBit(hidden_mask, i)) {
|
||||
list.push_back(std::make_unique<DropDownListStringItem>(strings[i], i, HasBit(disabled_mask, i)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!list.empty()) ShowDropDownList(w, std::move(list), selected, button, width);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 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 dropdown_func.h Functions related to the drop down widget. */
|
||||
|
||||
#ifndef WIDGETS_DROPDOWN_FUNC_H
|
||||
#define WIDGETS_DROPDOWN_FUNC_H
|
||||
|
||||
#include "../window_gui.h"
|
||||
|
||||
/* Show drop down menu containing a fixed list of strings */
|
||||
void ShowDropDownMenu(Window *w, const StringID *strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width = 0);
|
||||
|
||||
#endif /* WIDGETS_DROPDOWN_FUNC_H */
|
||||
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* 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 dropdown_type.h Types related to the drop down widget. */
|
||||
|
||||
#ifndef WIDGETS_DROPDOWN_TYPE_H
|
||||
#define WIDGETS_DROPDOWN_TYPE_H
|
||||
|
||||
#include "../window_type.h"
|
||||
#include "../gfx_func.h"
|
||||
#include "../gfx_type.h"
|
||||
#include "../palette_func.h"
|
||||
#include "../string_func.h"
|
||||
#include "../strings_func.h"
|
||||
#include "../table/strings.h"
|
||||
#include "../window_gui.h"
|
||||
|
||||
/**
|
||||
* Base list item class from which others are derived.
|
||||
*/
|
||||
class DropDownListItem {
|
||||
public:
|
||||
int result; ///< Result value to return to window on selection.
|
||||
bool masked; ///< Masked and unselectable item.
|
||||
bool shaded; ///< Shaded item, affects text colour.
|
||||
|
||||
explicit DropDownListItem(int result, bool masked = false, bool shaded = false) : result(result), masked(masked), shaded(shaded) {}
|
||||
virtual ~DropDownListItem() = default;
|
||||
|
||||
virtual bool Selectable() const { return true; }
|
||||
virtual uint Height() const { return 0; }
|
||||
virtual uint Width() const { return 0; }
|
||||
|
||||
virtual void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const
|
||||
{
|
||||
if (this->masked) GfxFillRect(full, GetColourGradient(bg_colour, SHADE_LIGHT), FILLRECT_CHECKER);
|
||||
}
|
||||
|
||||
TextColour GetColour(bool sel) const
|
||||
{
|
||||
if (this->shaded) return (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
|
||||
return sel ? TC_WHITE : TC_BLACK;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down divider component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TFs Font size -- used to determine height.
|
||||
*/
|
||||
template<class TBase, FontSize TFs = FS_NORMAL>
|
||||
class DropDownDivider : public TBase {
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownDivider(Args&&... args) : TBase(std::forward<Args>(args)...) {}
|
||||
|
||||
bool Selectable() const override { return false; }
|
||||
uint Height() const override { return std::max<uint>(GetCharacterHeight(TFs), this->TBase::Height()); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &, bool, Colours bg_colour) const override
|
||||
{
|
||||
uint8_t c1 = GetColourGradient(bg_colour, SHADE_DARK);
|
||||
uint8_t c2 = GetColourGradient(bg_colour, SHADE_LIGHTEST);
|
||||
|
||||
int mid = CenterBounds(full.top, full.bottom, 0);
|
||||
GfxFillRect(full.left, mid - WidgetDimensions::scaled.bevel.bottom, full.right, mid - 1, c1);
|
||||
GfxFillRect(full.left, mid, full.right, mid + WidgetDimensions::scaled.bevel.top - 1, c2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down string component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TFs Font size.
|
||||
* @tparam TEnd Position string at end if true, or start if false.
|
||||
*/
|
||||
template<class TBase, FontSize TFs = FS_NORMAL, bool TEnd = false>
|
||||
class DropDownString : public TBase {
|
||||
std::string string; ///< String to be drawn.
|
||||
Dimension dim; ///< Dimensions of string.
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownString(StringID string, Args&&... args) : TBase(std::forward<Args>(args)...)
|
||||
{
|
||||
this->SetString(GetString(string));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
explicit DropDownString(const std::string &string, Args&&... args) : TBase(std::forward<Args>(args)...)
|
||||
{
|
||||
SetDParamStr(0, string);
|
||||
this->SetString(GetString(STR_JUST_RAW_STRING));
|
||||
}
|
||||
|
||||
void SetString(std::string &&string)
|
||||
{
|
||||
this->string = std::move(string);
|
||||
this->dim = GetStringBoundingBox(this->string, TFs);
|
||||
}
|
||||
|
||||
uint Height() const override
|
||||
{
|
||||
return std::max<uint>(this->dim.height, this->TBase::Height());
|
||||
}
|
||||
|
||||
uint Width() const override { return this->dim.width + this->TBase::Width(); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
|
||||
{
|
||||
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
|
||||
DrawStringMultiLine(r.WithWidth(this->dim.width, rtl), this->string, this->GetColour(sel), SA_CENTER, false, TFs);
|
||||
this->TBase::Draw(full, r.Indent(this->dim.width, rtl), sel, bg_colour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Natural sorting comparator function for DropDownList::sort().
|
||||
* @param first Left side of comparison.
|
||||
* @param second Right side of comparison.
|
||||
* @return true if \a first precedes \a second.
|
||||
* @warning All items in the list need to be derivates of DropDownListStringItem.
|
||||
*/
|
||||
static bool NatSortFunc(std::unique_ptr<const DropDownListItem> const &first, std::unique_ptr<const DropDownListItem> const &second)
|
||||
{
|
||||
const std::string &str1 = static_cast<const DropDownString*>(first.get())->string;
|
||||
const std::string &str2 = static_cast<const DropDownString*>(second.get())->string;
|
||||
return StrNaturalCompare(str1, str2) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down icon component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TEnd Position icon at end if true, or start if false.
|
||||
*/
|
||||
template<class TBase, bool TEnd = false>
|
||||
class DropDownIcon : public TBase {
|
||||
SpriteID sprite; ///< Sprite ID to be drawn.
|
||||
PaletteID palette; ///< Palette ID to use.
|
||||
Dimension dsprite; ///< Bounding box dimensions of sprite.
|
||||
Dimension dbounds; ///< Bounding box dimensions of bounds.
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownIcon(SpriteID sprite, PaletteID palette, Args&&... args) : TBase(std::forward<Args>(args)...), sprite(sprite), palette(palette)
|
||||
{
|
||||
this->dsprite = GetSpriteSize(this->sprite);
|
||||
this->dbounds = this->dsprite;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
explicit DropDownIcon(const Dimension &dim, SpriteID sprite, PaletteID palette, Args&&... args) : TBase(std::forward<Args>(args)...), sprite(sprite), palette(palette), dbounds(dim)
|
||||
{
|
||||
this->dsprite = GetSpriteSize(this->sprite);
|
||||
}
|
||||
|
||||
uint Height() const override { return std::max(this->dbounds.height, this->TBase::Height()); }
|
||||
uint Width() const override { return this->dbounds.width + WidgetDimensions::scaled.hsep_normal + this->TBase::Width(); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
|
||||
{
|
||||
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
|
||||
Rect ir = r.WithWidth(this->dbounds.width, rtl);
|
||||
DrawSprite(this->sprite, this->palette, CenterBounds(ir.left, ir.right, this->dsprite.width), CenterBounds(r.top, r.bottom, this->dsprite.height));
|
||||
this->TBase::Draw(full, r.Indent(this->dbounds.width + WidgetDimensions::scaled.hsep_normal, rtl), sel, bg_colour);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop down checkmark component.
|
||||
* @tparam TBase Base component.
|
||||
* @tparam TFs Font size.
|
||||
* @tparam TEnd Position checkmark at end if true, or start if false.
|
||||
*/
|
||||
template<class TBase, bool TEnd = false, FontSize TFs = FS_NORMAL>
|
||||
class DropDownCheck : public TBase {
|
||||
bool checked; ///< Is item checked.
|
||||
Dimension dim; ///< Dimension of checkmark.
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit DropDownCheck(bool checked, Args&&... args) : TBase(std::forward<Args>(args)...), checked(checked)
|
||||
{
|
||||
this->dim = GetStringBoundingBox(STR_JUST_CHECKMARK, TFs);
|
||||
}
|
||||
|
||||
uint Height() const override { return std::max<uint>(this->dim.height, this->TBase::Height()); }
|
||||
uint Width() const override { return this->dim.width + WidgetDimensions::scaled.hsep_wide + this->TBase::Width(); }
|
||||
|
||||
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
|
||||
{
|
||||
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
|
||||
if (this->checked) {
|
||||
DrawStringMultiLine(r.WithWidth(this->dim.width, rtl), STR_JUST_CHECKMARK, this->GetColour(sel), SA_CENTER, false, TFs);
|
||||
}
|
||||
this->TBase::Draw(full, r.Indent(this->dim.width + WidgetDimensions::scaled.hsep_wide, rtl), sel, bg_colour);
|
||||
}
|
||||
};
|
||||
|
||||
/* Commonly used drop down list items. */
|
||||
using DropDownListDividerItem = DropDownDivider<DropDownListItem>;
|
||||
using DropDownListStringItem = DropDownString<DropDownListItem>;
|
||||
using DropDownListIconItem = DropDownIcon<DropDownString<DropDownListItem>>;
|
||||
using DropDownListCheckedItem = DropDownCheck<DropDownString<DropDownListItem>>;
|
||||
|
||||
/**
|
||||
* A drop down list is a collection of drop down list items.
|
||||
*/
|
||||
typedef std::vector<std::unique_ptr<const DropDownListItem>> DropDownList;
|
||||
|
||||
void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, WidgetID button, Rect wi_rect, Colours wi_colour, bool instant_close = false, bool persist = false);
|
||||
|
||||
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width = 0, bool instant_close = false, bool persist = false);
|
||||
|
||||
Dimension GetDropDownListDimension(const DropDownList &list);
|
||||
|
||||
void ReplaceDropDownList(Window *parent, DropDownList &&list);
|
||||
|
||||
#endif /* WIDGETS_DROPDOWN_TYPE_H */
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* 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 slider.cpp Implementation of the horizontal slider widget. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../palette_func.h"
|
||||
#include "../window_gui.h"
|
||||
#include "../window_func.h"
|
||||
#include "../strings_func.h"
|
||||
#include "../zoom_func.h"
|
||||
#include "slider_func.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
static const int SLIDER_WIDTH = 3;
|
||||
|
||||
/**
|
||||
* Draw a slider widget with knob at given value
|
||||
* @param r Rectangle to draw the widget in
|
||||
* @param min_value Minimum value of slider
|
||||
* @param max_value Maximum value of slider
|
||||
* @param value Value to put the slider at
|
||||
* @param labels List of positions and labels to draw along the slider.
|
||||
*/
|
||||
void DrawSliderWidget(Rect r, int min_value, int max_value, int value, const std::map<int, StringID> &labels)
|
||||
{
|
||||
/* Allow space for labels. We assume they are in the small font. */
|
||||
if (!labels.empty()) r.bottom -= GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.hsep_normal;
|
||||
|
||||
max_value -= min_value;
|
||||
|
||||
/* Draw a wedge indicating low to high value. */
|
||||
const int ha = (r.bottom - r.top) / 5;
|
||||
const int sw = ScaleGUITrad(SLIDER_WIDTH);
|
||||
const int t = WidgetDimensions::scaled.bevel.top; /* Thickness of lines */
|
||||
int wx1 = r.left + sw / 2;
|
||||
int wx2 = r.right - sw / 2;
|
||||
if (_current_text_dir == TD_RTL) std::swap(wx1, wx2);
|
||||
const uint shadow = GetColourGradient(COLOUR_GREY, SHADE_DARK);
|
||||
const uint fill = GetColourGradient(COLOUR_GREY, SHADE_LIGHTER);
|
||||
const uint light = GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST);
|
||||
const std::vector<Point> wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} };
|
||||
GfxFillPolygon(wedge, fill);
|
||||
GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light, t);
|
||||
GfxDrawLine(wedge[1].x, wedge[1].y, wedge[2].x, wedge[2].y, _current_text_dir == TD_RTL ? shadow : light, t);
|
||||
GfxDrawLine(wedge[0].x, wedge[0].y, wedge[1].x, wedge[1].y, shadow, t);
|
||||
|
||||
int x;
|
||||
for (auto label : labels) {
|
||||
x = label.first - min_value;
|
||||
if (_current_text_dir == TD_RTL) x = max_value - x;
|
||||
x = r.left + (x * (r.right - r.left - sw) / max_value) + sw / 2;
|
||||
GfxDrawLine(x, r.bottom - ha + 1, x, r.bottom + (label.second == STR_NULL ? 0 : WidgetDimensions::scaled.hsep_normal), shadow, t);
|
||||
if (label.second != STR_NULL) {
|
||||
Dimension d = GetStringBoundingBox(label.second, FS_SMALL);
|
||||
x = Clamp(x - d.width / 2, r.left, r.right - d.width);
|
||||
DrawString(x, x + d.width, r.bottom + 1 + WidgetDimensions::scaled.hsep_normal, label.second, TC_BLACK, SA_CENTER, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw a slider handle indicating current value. */
|
||||
value -= min_value;
|
||||
if (_current_text_dir == TD_RTL) value = max_value - value;
|
||||
x = r.left + (value * (r.right - r.left - sw) / max_value);
|
||||
DrawFrameRect(x, r.top, x + sw, r.bottom, COLOUR_GREY, FR_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on a slider widget to change the value
|
||||
* @param r Rectangle of the widget
|
||||
* @param pt Clicked point
|
||||
* @param value[in,out] Value to modify
|
||||
* @return True if the value setting was modified
|
||||
*/
|
||||
bool ClickSliderWidget(Rect r, Point pt, int min_value, int max_value, int &value)
|
||||
{
|
||||
max_value -= min_value;
|
||||
|
||||
const int sw = ScaleGUITrad(SLIDER_WIDTH);
|
||||
int new_value = Clamp((pt.x - r.left - sw / 2) * max_value / (r.right - r.left - sw), 0, max_value);
|
||||
if (_current_text_dir == TD_RTL) new_value = max_value - new_value;
|
||||
new_value += min_value;
|
||||
|
||||
if (new_value != value) {
|
||||
value = new_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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 slider_type.h Types related to the horizontal slider widget. */
|
||||
|
||||
#ifndef WIDGETS_SLIDER_TYPE_H
|
||||
#define WIDGETS_SLIDER_TYPE_H
|
||||
|
||||
#include "../window_type.h"
|
||||
#include "../gfx_func.h"
|
||||
|
||||
void DrawSliderWidget(Rect r, int min_value, int max_value, int value, const std::map<int, StringID> &labels);
|
||||
bool ClickSliderWidget(Rect r, Point pt, int min_value, int max_value, int &value);
|
||||
|
||||
inline bool ClickSliderWidget(Rect r, Point pt, int min_value, int max_value, uint8_t &value)
|
||||
{
|
||||
int tmp_value = value;
|
||||
if (!ClickSliderWidget(r, pt, min_value, max_value, tmp_value)) return false;
|
||||
value = tmp_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* WIDGETS_SLIDER_TYPE_H */
|
||||
Reference in New Issue
Block a user