1147 lines
42 KiB
C++
1147 lines
42 KiB
C++
#include "../CompanyManager.h"
|
|
#include "../Date.h"
|
|
#include "../Entities/EntityManager.h"
|
|
#include "../Graphics/Colour.h"
|
|
#include "../Graphics/ImageIds.h"
|
|
#include "../Input.h"
|
|
#include "../Interop/Interop.hpp"
|
|
#include "../Localisation/FormatArguments.hpp"
|
|
#include "../Localisation/StringIds.h"
|
|
#include "../Objects/CargoObject.h"
|
|
#include "../Objects/CompetitorObject.h"
|
|
#include "../Objects/InterfaceSkinObject.h"
|
|
#include "../OpenLoco.h"
|
|
#include "../StationManager.h"
|
|
#include "../Ui/Dropdown.h"
|
|
#include "../Ui/WindowManager.h"
|
|
#include "../Utility/String.hpp"
|
|
#include "../Vehicles/Orders.h"
|
|
#include "../Vehicles/Vehicle.h"
|
|
#include "../Widget.h"
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
|
|
using namespace OpenLoco::Interop;
|
|
|
|
namespace OpenLoco::Ui::Windows::VehicleList
|
|
{
|
|
static loco_global<VehicleType, 0x00525FAF> _lastVehiclesOption;
|
|
|
|
static const Gfx::ui_size_t window_size = { 550, 213 };
|
|
static const Gfx::ui_size_t max_dimensions = { 550, 1200 };
|
|
static const Gfx::ui_size_t min_dimensions = { 220, 160 };
|
|
|
|
static WindowEventList _events;
|
|
|
|
enum Widx
|
|
{
|
|
frame = 0,
|
|
caption = 1,
|
|
close_button = 2,
|
|
panel = 3,
|
|
tab_trains,
|
|
tab_buses,
|
|
tab_trucks,
|
|
tab_trams,
|
|
tab_aircraft,
|
|
tab_ships,
|
|
company_select,
|
|
sort_name,
|
|
sort_profit,
|
|
sort_age,
|
|
sort_reliability,
|
|
scrollview,
|
|
filter_type,
|
|
filter_type_btn,
|
|
cargo_type,
|
|
cargo_type_btn,
|
|
};
|
|
|
|
Widget _widgets[] = {
|
|
makeWidget({ 0, 0 }, { 550, 213 }, WidgetType::frame, WindowColour::primary),
|
|
makeWidget({ 1, 1 }, { 548, 13 }, WidgetType::caption_24, WindowColour::primary),
|
|
makeWidget({ 535, 2 }, { 13, 13 }, WidgetType::wt_9, WindowColour::primary, ImageIds::close_button, StringIds::tooltip_close_window),
|
|
makeWidget({ 0, 41 }, { 550, 172 }, WidgetType::panel, WindowColour::secondary),
|
|
makeRemapWidget({ 3, 15 }, { 31, 27 }, WidgetType::wt_8, WindowColour::secondary, ImageIds::tab, StringIds::tooltip_trains),
|
|
makeRemapWidget({ 3, 15 }, { 31, 27 }, WidgetType::wt_8, WindowColour::secondary, ImageIds::tab, StringIds::tooltip_buses),
|
|
makeRemapWidget({ 3, 15 }, { 31, 27 }, WidgetType::wt_8, WindowColour::secondary, ImageIds::tab, StringIds::tooltip_trucks),
|
|
makeRemapWidget({ 3, 15 }, { 31, 27 }, WidgetType::wt_8, WindowColour::secondary, ImageIds::tab, StringIds::tooltip_trams),
|
|
makeRemapWidget({ 3, 15 }, { 31, 27 }, WidgetType::wt_8, WindowColour::secondary, ImageIds::tab, StringIds::tooltip_aircraft),
|
|
makeRemapWidget({ 3, 15 }, { 31, 27 }, WidgetType::wt_8, WindowColour::secondary, ImageIds::tab, StringIds::tooltip_ships),
|
|
makeWidget({ 0, 14 }, { 26, 26 }, WidgetType::wt_9, WindowColour::primary, StringIds::null, StringIds::tooltip_select_company),
|
|
makeWidget({ 4, 43 }, { 310, 12 }, WidgetType::wt_14, WindowColour::secondary, StringIds::null, StringIds::tooltip_sort_by_name),
|
|
makeWidget({ 314, 43 }, { 100, 12 }, WidgetType::wt_14, WindowColour::secondary, StringIds::null, StringIds::tooltip_sort_by_profit),
|
|
makeWidget({ 414, 43 }, { 65, 12 }, WidgetType::wt_14, WindowColour::secondary, StringIds::null, StringIds::tooltip_sort_by_age),
|
|
makeWidget({ 479, 43 }, { 67, 12 }, WidgetType::wt_14, WindowColour::secondary, StringIds::null, StringIds::tooltip_sort_by_reliability),
|
|
makeWidget({ 3, 56 }, { 544, 138 }, WidgetType::scrollview, WindowColour::secondary, Scrollbars::vertical),
|
|
makeDropdownWidgets({ 280 - 16, 200 }, { 120, 12 }, WidgetType::wt_18, WindowColour::secondary, StringIds::empty),
|
|
makeDropdownWidgets({ 402 - 16, 200 }, { 150, 12 }, WidgetType::wt_18, WindowColour::secondary, StringIds::empty),
|
|
widgetEnd()
|
|
};
|
|
|
|
// clang-format off
|
|
constexpr uint16_t _tabWidgets = (1 << Widx::tab_trains) | (1 << Widx::tab_buses) | (1 << Widx::tab_trucks) | (1 << Widx::tab_trams) | (1 << Widx::tab_aircraft) | (1 << Widx::tab_ships);
|
|
constexpr uint64_t _enabledWidgets = (1ULL << Widx::close_button) | _tabWidgets | (1ULL << Widx::company_select) |
|
|
(1ULL << Widx::sort_name) | (1ULL << Widx::sort_profit) | (1ULL << Widx::sort_age) | (1ULL << Widx::sort_reliability) |
|
|
(1ULL << Widx::scrollview) | (1ULL << Widx::filter_type) | (1ULL << Widx::filter_type_btn) | (1ULL << Widx::cargo_type) | (1ULL << Widx::cargo_type_btn);
|
|
// clang-format on
|
|
|
|
enum SortMode : uint16_t
|
|
{
|
|
Name,
|
|
Profit,
|
|
Age,
|
|
Reliability,
|
|
};
|
|
|
|
enum FilterMode : uint8_t
|
|
{
|
|
allVehicles,
|
|
stoppingAt,
|
|
transportingCargo,
|
|
};
|
|
|
|
static const uint8_t row_heights[] = {
|
|
28,
|
|
28,
|
|
28,
|
|
28,
|
|
48,
|
|
36
|
|
};
|
|
|
|
static Widx getTabFromType(VehicleType type);
|
|
static void initEvents();
|
|
|
|
constexpr bool isStationFilterActive(const Window* self, bool checkSelection = true)
|
|
{
|
|
return self->var_88A == static_cast<int16_t>(FilterMode::stoppingAt) && (!checkSelection || self->var_88C != -1);
|
|
}
|
|
|
|
constexpr bool isCargoFilterActive(const Window* self, bool checkSelection = true)
|
|
{
|
|
return self->var_88A == static_cast<int16_t>(FilterMode::transportingCargo) && (!checkSelection || self->var_88C != -1);
|
|
}
|
|
|
|
static bool refreshActiveStation(Window* self)
|
|
{
|
|
if (!isStationFilterActive(self, false))
|
|
return false;
|
|
|
|
auto stationWindow = WindowManager::find(WindowType::station);
|
|
if (stationWindow != nullptr)
|
|
{
|
|
self->var_88C = stationWindow->number;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
self->var_88C = -1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
using Vehicles::VehicleHead;
|
|
|
|
static bool vehicleStopsAtActiveStation(const VehicleHead* head, StationId_t filterStationId)
|
|
{
|
|
auto orders = Vehicles::OrderRingView(head->orderTableOffset);
|
|
for (auto& order : orders)
|
|
{
|
|
auto* stationOrder = order.as<Vehicles::OrderStation>();
|
|
if (stationOrder == nullptr)
|
|
continue;
|
|
|
|
const auto stationId = stationOrder->getStation();
|
|
if (stationId == filterStationId)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool vehicleIsTransportingCargo(const VehicleHead* head, int16_t filterCargoId)
|
|
{
|
|
auto orders = Vehicles::OrderRingView(head->orderTableOffset);
|
|
for (auto& order : orders)
|
|
{
|
|
Vehicles::OrderCargo* cargoOrder = order.as<Vehicles::OrderUnloadAll>();
|
|
if (cargoOrder == nullptr)
|
|
cargoOrder = order.as<Vehicles::OrderWaitFor>();
|
|
if (cargoOrder == nullptr)
|
|
continue;
|
|
|
|
const auto cargoId = cargoOrder->getCargo();
|
|
if (cargoId == filterCargoId)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 0x004C1D4F
|
|
static void refreshVehicleList(Window* self)
|
|
{
|
|
refreshActiveStation(self);
|
|
self->row_count = 0;
|
|
for (auto vehicle : EntityManager::VehicleList())
|
|
{
|
|
if (vehicle->vehicleType != static_cast<VehicleType>(self->current_tab))
|
|
continue;
|
|
|
|
if (vehicle->owner != self->number)
|
|
continue;
|
|
|
|
if (isStationFilterActive(self) && !vehicleStopsAtActiveStation(vehicle, self->var_88C))
|
|
continue;
|
|
|
|
if (isCargoFilterActive(self) && !vehicleIsTransportingCargo(vehicle, self->var_88C))
|
|
continue;
|
|
|
|
vehicle->var_0C &= ~Vehicles::Flags0C::sorted;
|
|
}
|
|
}
|
|
|
|
// 0x004C1E4F
|
|
static bool orderByName(const VehicleHead& lhs, const VehicleHead& rhs)
|
|
{
|
|
char lhsString[256] = { 0 };
|
|
auto args = FormatArguments::common(lhs.ordinalNumber);
|
|
StringManager::formatString(lhsString, lhs.name, &args);
|
|
|
|
char rhsString[256] = { 0 };
|
|
args = FormatArguments::common(rhs.ordinalNumber);
|
|
StringManager::formatString(rhsString, rhs.name, &args);
|
|
|
|
return Utility::strlogicalcmp(lhsString, rhsString) < 0;
|
|
}
|
|
|
|
// 0x004C1EC9
|
|
static bool orderByProfit(const VehicleHead& lhs, const VehicleHead& rhs)
|
|
{
|
|
auto profitL = Vehicles::Vehicle(&lhs).veh2->totalRecentProfit();
|
|
auto profitR = Vehicles::Vehicle(&rhs).veh2->totalRecentProfit();
|
|
|
|
return profitR - profitL < 0;
|
|
}
|
|
|
|
// 0x004C1F1E
|
|
static bool orderByAge(const VehicleHead& lhs, const VehicleHead& rhs)
|
|
{
|
|
auto dayCreatedL = Vehicles::Vehicle(&lhs).veh1->dayCreated;
|
|
auto dayCreatedR = Vehicles::Vehicle(&rhs).veh1->dayCreated;
|
|
|
|
return static_cast<int32_t>(dayCreatedL - dayCreatedR) < 0;
|
|
}
|
|
|
|
// 0x004C1F45
|
|
static bool orderByReliability(const VehicleHead& lhs, const VehicleHead& rhs)
|
|
{
|
|
auto reliabilityL = Vehicles::Vehicle(&lhs).veh2->reliability;
|
|
auto reliabilityR = Vehicles::Vehicle(&rhs).veh2->reliability;
|
|
|
|
return static_cast<int32_t>(reliabilityR - reliabilityL) < 0;
|
|
}
|
|
|
|
static bool getOrder(const SortMode mode, const VehicleHead& lhs, const VehicleHead& rhs)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case SortMode::Name:
|
|
return orderByName(lhs, rhs);
|
|
|
|
case SortMode::Profit:
|
|
return orderByProfit(lhs, rhs);
|
|
|
|
case SortMode::Age:
|
|
return orderByAge(lhs, rhs);
|
|
|
|
case SortMode::Reliability:
|
|
return orderByReliability(lhs, rhs);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// 0x004C1D92
|
|
static void updateVehicleList(Window* self)
|
|
{
|
|
int16_t insertId = -1;
|
|
|
|
for (auto vehicle : EntityManager::VehicleList())
|
|
{
|
|
if (vehicle->vehicleType != static_cast<VehicleType>(self->current_tab))
|
|
continue;
|
|
|
|
if (vehicle->owner != self->number)
|
|
continue;
|
|
|
|
if (vehicle->var_0C & Vehicles::Flags0C::sorted)
|
|
continue;
|
|
|
|
if (isStationFilterActive(self) && !vehicleStopsAtActiveStation(vehicle, self->var_88C))
|
|
continue;
|
|
|
|
if (isCargoFilterActive(self) && !vehicleIsTransportingCargo(vehicle, self->var_88C))
|
|
continue;
|
|
|
|
if (insertId == -1)
|
|
{
|
|
insertId = vehicle->id;
|
|
continue;
|
|
}
|
|
|
|
auto insertVehicle = EntityManager::get<VehicleHead>(insertId);
|
|
if (getOrder(SortMode(self->sort_mode), *vehicle, *insertVehicle))
|
|
{
|
|
insertId = vehicle->id;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (insertId != -1)
|
|
{
|
|
auto vehicle = EntityManager::get<VehicleHead>(insertId);
|
|
vehicle->var_0C |= Vehicles::Flags0C::sorted;
|
|
|
|
if (vehicle->id != self->row_info[self->row_count])
|
|
self->row_info[self->row_count] = vehicle->id;
|
|
|
|
self->row_count++;
|
|
|
|
if (self->row_count > self->var_83C)
|
|
self->var_83C = self->row_count;
|
|
}
|
|
else
|
|
{
|
|
if (self->var_83C != self->row_count)
|
|
self->var_83C = self->row_count;
|
|
|
|
refreshVehicleList(self);
|
|
}
|
|
}
|
|
|
|
// 0x004C2A6E
|
|
static void drawTabs(Window* self, Gfx::Context* context)
|
|
{
|
|
auto skin = ObjectManager::get<InterfaceSkinObject>();
|
|
auto companyColour = CompanyManager::getCompanyColour(self->number);
|
|
|
|
static std::pair<WidgetIndex_t, std::array<uint32_t, 8>> tabAnimations[] = {
|
|
{ Widx::tab_trains, {
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_0,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_1,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_2,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_3,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_4,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_5,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_6,
|
|
InterfaceSkin::ImageIds::vehicle_train_frame_7,
|
|
} },
|
|
{ Widx::tab_aircraft, {
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_0,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_1,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_2,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_3,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_4,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_5,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_6,
|
|
InterfaceSkin::ImageIds::vehicle_aircraft_frame_7,
|
|
} },
|
|
{ Widx::tab_buses, {
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_0,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_1,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_2,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_3,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_4,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_5,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_6,
|
|
InterfaceSkin::ImageIds::vehicle_buses_frame_7,
|
|
} },
|
|
{ Widx::tab_trams, {
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_0,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_1,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_2,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_3,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_4,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_5,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_6,
|
|
InterfaceSkin::ImageIds::vehicle_trams_frame_7,
|
|
} },
|
|
{ Widx::tab_trucks, {
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_0,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_1,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_2,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_3,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_4,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_5,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_6,
|
|
InterfaceSkin::ImageIds::vehicle_trucks_frame_7,
|
|
} },
|
|
{ Widx::tab_ships, {
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_0,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_1,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_2,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_3,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_4,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_5,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_6,
|
|
InterfaceSkin::ImageIds::vehicle_ships_frame_7,
|
|
} },
|
|
};
|
|
|
|
for (auto [tab, frames] : tabAnimations)
|
|
{
|
|
if (self->isDisabled(tab))
|
|
continue;
|
|
|
|
auto isActive = tab == self->current_tab + Widx::tab_trains;
|
|
auto imageId = isActive ? frames[self->frame_no / 2 % 8] : frames[0];
|
|
|
|
uint32_t image = Gfx::recolour(skin->img + imageId, companyColour);
|
|
Widget::drawTab(self, context, image, tab);
|
|
}
|
|
}
|
|
|
|
// 0x004C28A5
|
|
static void disableUnavailableVehicleTypes(Window* self)
|
|
{
|
|
// The original game looks at all companies here. We only look at the current company instead.
|
|
auto company = CompanyManager::get(self->number);
|
|
|
|
// Disable the tabs for the vehicles that are _not_ available for this company.
|
|
self->disabled_widgets = (company->available_vehicles ^ 0x3F) << Widx::tab_trains;
|
|
}
|
|
|
|
// 0x004C1AA2
|
|
static Window* create(CompanyId_t companyId)
|
|
{
|
|
Window* self = WindowManager::createWindow(
|
|
WindowType::vehicleList,
|
|
window_size,
|
|
WindowFlags::flag_11,
|
|
&_events);
|
|
|
|
self->widgets = _widgets;
|
|
self->enabled_widgets = _enabledWidgets;
|
|
self->number = companyId;
|
|
self->owner = companyId;
|
|
self->frame_no = 0;
|
|
|
|
auto skin = ObjectManager::get<InterfaceSkinObject>();
|
|
self->setColour(WindowColour::secondary, skin->colour_0A);
|
|
|
|
disableUnavailableVehicleTypes(self);
|
|
|
|
return self;
|
|
}
|
|
|
|
// 0x004C19DC
|
|
Window* open(CompanyId_t companyId, VehicleType type)
|
|
{
|
|
Window* self = WindowManager::bringToFront(WindowType::vehicleList, companyId);
|
|
if (self != nullptr)
|
|
{
|
|
self->callOnMouseUp(VehicleList::getTabFromType(type));
|
|
return self;
|
|
}
|
|
|
|
initEvents();
|
|
|
|
// 0x004C1A05
|
|
self = create(companyId);
|
|
auto tabIndex = static_cast<uint8_t>(type);
|
|
self->current_tab = tabIndex;
|
|
self->row_height = row_heights[tabIndex];
|
|
self->width = window_size.width;
|
|
self->height = window_size.height;
|
|
self->sort_mode = 0;
|
|
self->var_83C = 0;
|
|
self->row_hover = -1;
|
|
self->var_88A = static_cast<int16_t>(FilterMode::allVehicles);
|
|
self->var_88C = -1;
|
|
|
|
refreshVehicleList(self);
|
|
|
|
self->invalidate();
|
|
|
|
self->callOnResize();
|
|
self->callPrepareDraw();
|
|
self->initScrollWidgets();
|
|
|
|
return self;
|
|
}
|
|
|
|
static Widx getTabFromType(VehicleType type)
|
|
{
|
|
auto tabIndex = static_cast<uint8_t>(type);
|
|
if (tabIndex > 5)
|
|
{
|
|
throw std::domain_error("Unexpected vehicle type");
|
|
}
|
|
|
|
static constexpr Widx type_to_widx[] = {
|
|
tab_trains,
|
|
tab_buses,
|
|
tab_trucks,
|
|
tab_trams,
|
|
tab_aircraft,
|
|
tab_ships,
|
|
};
|
|
return type_to_widx[tabIndex];
|
|
}
|
|
|
|
// 0x4C2865
|
|
static void setTransportTypeTabs(Window* self)
|
|
{
|
|
auto disabledWidgets = self->disabled_widgets >> Widx::tab_trains;
|
|
auto widget = self->widgets + Widx::tab_trains;
|
|
auto tabWidth = widget->right - widget->left;
|
|
auto tabX = widget->left;
|
|
for (auto i = 0; i <= Widx::tab_ships - Widx::tab_trains; ++i, ++widget)
|
|
{
|
|
if (disabledWidgets & (1ULL << i))
|
|
{
|
|
widget->type = WidgetType::none;
|
|
}
|
|
else
|
|
{
|
|
widget->type = WidgetType::wt_8;
|
|
widget->left = tabX;
|
|
widget->right = tabX + tabWidth;
|
|
tabX += tabWidth + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 0x004C1F88
|
|
static void prepareDraw(Window* self)
|
|
{
|
|
// The original game was setting widget sets here. As all tabs are the same, this has been omitted.
|
|
self->activated_widgets &= ~_tabWidgets;
|
|
self->activated_widgets |= 1ULL << (self->current_tab + Widx::tab_trains);
|
|
|
|
auto company = CompanyManager::get(self->number);
|
|
[[maybe_unused]] auto args = FormatArguments::common(company->name);
|
|
|
|
static constexpr string_id typeToCaption[] = {
|
|
StringIds::stringid_trains,
|
|
StringIds::stringid_buses,
|
|
StringIds::stringid_trucks,
|
|
StringIds::stringid_trams,
|
|
StringIds::stringid_aircraft,
|
|
StringIds::stringid_ships,
|
|
};
|
|
|
|
// Basic frame widget dimensions
|
|
self->widgets[Widx::frame].right = self->width - 1;
|
|
self->widgets[Widx::frame].bottom = self->height - 1;
|
|
|
|
self->widgets[Widx::panel].right = self->width - 1;
|
|
self->widgets[Widx::panel].bottom = self->height - 1;
|
|
|
|
self->widgets[Widx::caption].right = self->width - 2;
|
|
self->widgets[Widx::caption].text = typeToCaption[self->current_tab];
|
|
|
|
self->widgets[Widx::close_button].left = self->width - 15;
|
|
self->widgets[Widx::close_button].right = self->width - 3;
|
|
|
|
self->widgets[Widx::scrollview].right = self->width - 4;
|
|
self->widgets[Widx::scrollview].bottom = self->height - 14;
|
|
|
|
// Reposition table headers
|
|
self->widgets[Widx::sort_name].right = std::min(self->width - 4, 313);
|
|
|
|
self->widgets[Widx::sort_profit].left = std::min(self->width - 4, 314);
|
|
self->widgets[Widx::sort_profit].right = std::min(self->width - 4, 413);
|
|
|
|
self->widgets[Widx::sort_age].left = std::min(self->width - 4, 414);
|
|
self->widgets[Widx::sort_age].right = std::min(self->width - 4, 478);
|
|
|
|
self->widgets[Widx::sort_reliability].left = std::min(self->width - 4, 479);
|
|
self->widgets[Widx::sort_reliability].right = std::min(self->width - 4, 545);
|
|
|
|
// Reposition company selection
|
|
self->widgets[Widx::company_select].left = self->width - 28;
|
|
self->widgets[Widx::company_select].right = self->width - 3;
|
|
|
|
// Set header button captions.
|
|
self->widgets[Widx::sort_name].text = self->sort_mode == SortMode::Name ? StringIds::table_header_name_desc : StringIds::table_header_name;
|
|
self->widgets[Widx::sort_profit].text = self->sort_mode == SortMode::Profit ? StringIds::table_header_monthly_profit_desc : StringIds::table_header_monthly_profit;
|
|
self->widgets[Widx::sort_age].text = self->sort_mode == SortMode::Age ? StringIds::table_header_age_desc : StringIds::table_header_age;
|
|
self->widgets[Widx::sort_reliability].text = self->sort_mode == SortMode::Reliability ? StringIds::table_header_reliability_desc : StringIds::table_header_reliability;
|
|
|
|
// Reposition filter dropdowns
|
|
self->widgets[Widx::filter_type].top = self->height - 13;
|
|
self->widgets[Widx::filter_type].bottom = self->height - 2;
|
|
|
|
self->widgets[Widx::filter_type_btn].top = self->height - 12;
|
|
self->widgets[Widx::filter_type_btn].bottom = self->height - 3;
|
|
|
|
self->widgets[Widx::cargo_type].top = self->height - 13;
|
|
self->widgets[Widx::cargo_type].bottom = self->height - 2;
|
|
|
|
self->widgets[Widx::cargo_type_btn].top = self->height - 12;
|
|
self->widgets[Widx::cargo_type_btn].bottom = self->height - 3;
|
|
|
|
// Disable cargo dropdown if not applicable
|
|
if (self->var_88A != FilterMode::transportingCargo)
|
|
self->disabled_widgets |= (1 << Widx::cargo_type) | (1 << Widx::cargo_type_btn);
|
|
else
|
|
self->disabled_widgets &= ~((1 << Widx::cargo_type) | (1 << Widx::cargo_type_btn));
|
|
|
|
// Set appropriate tooltip
|
|
static constexpr std::array<string_id, 3> filterTooltipByType = {
|
|
StringIds::null,
|
|
StringIds::tooltip_open_station_window_to_filter,
|
|
StringIds::tooltip_select_cargo_type,
|
|
};
|
|
self->widgets[Widx::cargo_type_btn].tooltip = filterTooltipByType[self->var_88A];
|
|
|
|
setTransportTypeTabs(self);
|
|
}
|
|
|
|
// 0x004C211C
|
|
static void draw(Window* self, Gfx::Context* context)
|
|
{
|
|
self->draw(context);
|
|
drawTabs(self, context);
|
|
|
|
// Draw company owner image.
|
|
auto company = CompanyManager::get(self->number);
|
|
auto competitorObj = ObjectManager::get<CompetitorObject>(company->competitor_id);
|
|
uint32_t image = Gfx::recolour(competitorObj->images[company->owner_emotion], company->mainColours.primary);
|
|
uint16_t x = self->x + self->widgets[Widx::company_select].left + 1;
|
|
uint16_t y = self->y + self->widgets[Widx::company_select].top + 1;
|
|
Gfx::drawImage(context, x, y, image);
|
|
|
|
static constexpr std::pair<string_id, string_id> typeToFooterStringIds[]{
|
|
{ StringIds::num_trains_singular, StringIds::num_trains_plural },
|
|
{ StringIds::num_buses_singular, StringIds::num_buses_plural },
|
|
{ StringIds::num_trucks_singular, StringIds::num_trucks_plural },
|
|
{ StringIds::num_trams_singular, StringIds::num_trams_plural },
|
|
{ StringIds::num_aircrafts_singular, StringIds::num_aircrafts_plural },
|
|
{ StringIds::num_ships_singular, StringIds::num_ships_plural },
|
|
};
|
|
|
|
FormatArguments args = {};
|
|
|
|
{
|
|
auto& footerStringPair = typeToFooterStringIds[self->current_tab];
|
|
string_id footerStringId = self->var_83C == 1 ? footerStringPair.first : footerStringPair.second;
|
|
|
|
args = FormatArguments::common(footerStringId, self->var_83C);
|
|
Gfx::drawString_494B3F(*context, self->x + 3, self->y + self->height - 13, Colour::black, StringIds::black_stringid, &args);
|
|
}
|
|
|
|
static constexpr std::array<string_id, 3> typeToFilterStringIds{
|
|
StringIds::all_vehicles,
|
|
StringIds::stopping_at_station,
|
|
StringIds::transporting_cargo,
|
|
};
|
|
|
|
{
|
|
// Show current filter type
|
|
string_id filter = typeToFilterStringIds[self->var_88A];
|
|
args = FormatArguments::common(filter);
|
|
auto* widget = &self->widgets[Widx::filter_type];
|
|
Gfx::drawString_494BBF(*context, self->x + widget->left + 1, self->y + widget->top, widget->width() - 15, Colour::black, StringIds::wcolour2_stringid, &args);
|
|
}
|
|
|
|
auto* widget = &self->widgets[Widx::cargo_type];
|
|
auto xPos = self->x + widget->left + 1;
|
|
bool filterActive = false;
|
|
|
|
if (isStationFilterActive(self, false))
|
|
{
|
|
filterActive = true;
|
|
if (self->var_88C != -1)
|
|
{
|
|
auto station = StationManager::get(self->var_88C);
|
|
args = FormatArguments::common(station->name, station->town);
|
|
}
|
|
else
|
|
{
|
|
args = FormatArguments::common(StringIds::no_station_selected);
|
|
}
|
|
}
|
|
|
|
else if (isCargoFilterActive(self, false))
|
|
{
|
|
filterActive = true;
|
|
if (self->var_88C != -1)
|
|
{
|
|
// Show current cargo
|
|
auto cargoObj = ObjectManager::get<CargoObject>(self->var_88C);
|
|
args = FormatArguments::common(StringIds::carrying_cargoid_sprite, cargoObj->name, cargoObj->unit_inline_sprite);
|
|
|
|
// NB: the -9 in the xpos is to compensate for a hack due to the cargo dropdown limitation (only three args per item)
|
|
xPos = self->x + widget->left - 9;
|
|
}
|
|
else
|
|
{
|
|
args = FormatArguments::common(StringIds::no_cargo_selected);
|
|
}
|
|
}
|
|
|
|
if (filterActive)
|
|
{
|
|
// Draw filter text as prepared
|
|
Gfx::drawString_494BBF(*context, xPos, self->y + widget->top, widget->width() - 15, Colour::black, StringIds::wcolour2_stringid, &args);
|
|
}
|
|
}
|
|
|
|
// 0x004B6D43
|
|
static void drawVehicle(VehicleHead* vehicle, Gfx::Context* context, uint16_t yPos)
|
|
{
|
|
registers regs;
|
|
regs.esi = (int32_t)vehicle;
|
|
regs.edi = (int32_t)context;
|
|
regs.al = 0x40;
|
|
regs.cx = 0;
|
|
regs.dx = yPos;
|
|
call(0x004B6D43, regs);
|
|
}
|
|
|
|
// 0x004C21CD
|
|
static void drawScroll(Window* self, Gfx::Context* context, uint32_t scrollIndex)
|
|
{
|
|
auto shade = Colour::getShade(self->getColour(WindowColour::secondary), 1);
|
|
Gfx::clearSingle(*context, shade);
|
|
|
|
auto yPos = 0;
|
|
for (auto i = 0; i < self->var_83C; i++)
|
|
{
|
|
auto vehicleId = self->row_info[i];
|
|
|
|
// Item not in rendering context, or no vehicle available for this slot?
|
|
if (yPos + self->row_height < context->y || yPos >= context->y + context->height + self->row_height || vehicleId == -1)
|
|
{
|
|
yPos += self->row_height;
|
|
continue;
|
|
}
|
|
|
|
auto head = EntityManager::get<VehicleHead>(vehicleId);
|
|
|
|
// Highlight selection.
|
|
if (head->id == self->row_hover)
|
|
Gfx::drawRect(context, 0, yPos, self->width, self->row_height, Colour::getShade(self->getColour(WindowColour::secondary), 0));
|
|
|
|
// Draw vehicle at the bottom of the row.
|
|
drawVehicle(head, context, yPos + (self->row_height - 28) / 2 + 6);
|
|
|
|
// Draw vehicle status
|
|
{
|
|
// Prepare status for drawing
|
|
auto status = head->getStatus();
|
|
auto args = FormatArguments::common();
|
|
args.push(head->name);
|
|
args.push(head->ordinalNumber);
|
|
args.push(status.status1);
|
|
args.push(status.status1Args);
|
|
args.push(status.status2);
|
|
args.push(status.status2Args);
|
|
|
|
string_id format = StringIds::vehicle_list_status_2pos;
|
|
if (status.status2 != StringIds::null)
|
|
format = StringIds::vehicle_list_status_3pos;
|
|
|
|
// Draw status
|
|
yPos += 2;
|
|
Gfx::drawString_494BBF(*context, 1, yPos, 308, Colour::outline(Colour::black), format, &args);
|
|
}
|
|
|
|
auto vehicle = Vehicles::Vehicle(head);
|
|
|
|
// Vehicle profit
|
|
{
|
|
string_id format = StringIds::vehicle_list_profit_pos;
|
|
currency32_t profit = vehicle.veh2->totalRecentProfit() / 4;
|
|
if (profit < 0)
|
|
{
|
|
format = StringIds::vehicle_list_profit_neg;
|
|
profit *= -1;
|
|
}
|
|
|
|
auto args = FormatArguments::common(profit);
|
|
Gfx::drawString_494BBF(*context, 310, yPos, 98, Colour::outline(Colour::black), format, &args);
|
|
}
|
|
|
|
// Vehicle age
|
|
{
|
|
string_id format = StringIds::vehicle_list_age_years;
|
|
auto age = (getCurrentDay() - vehicle.veh1->dayCreated) / 365;
|
|
if (age == 1)
|
|
format = StringIds::vehicle_list_age_year;
|
|
|
|
auto args = FormatArguments::common(age);
|
|
Gfx::drawString_494BBF(*context, 410, yPos, 63, Colour::outline(Colour::black), format, &args);
|
|
}
|
|
|
|
// Vehicle reliability
|
|
{
|
|
int16_t reliability = vehicle.veh2->reliability;
|
|
auto args = FormatArguments::common(reliability);
|
|
Gfx::drawString_494BBF(*context, 475, yPos, 65, Colour::outline(Colour::black), StringIds::vehicle_list_reliability, &args);
|
|
}
|
|
|
|
yPos += self->row_height - 2;
|
|
}
|
|
}
|
|
|
|
// 0x004C24F7
|
|
static void switchTab(Window* self, VehicleType type)
|
|
{
|
|
if (Input::isToolActive(self->type, self->number))
|
|
Input::toolCancel();
|
|
|
|
auto tabIndex = static_cast<uint8_t>(type);
|
|
self->current_tab = tabIndex;
|
|
self->row_height = row_heights[tabIndex];
|
|
self->frame_no = 0;
|
|
|
|
if (CompanyManager::getControllingId() == self->number && _lastVehiclesOption != type)
|
|
{
|
|
*_lastVehiclesOption = type;
|
|
WindowManager::invalidate(WindowType::topToolbar);
|
|
}
|
|
|
|
// The original game was setting viewports and (enabled/disabled) widgets here.
|
|
// As all tabs are the same, we've simplified this.
|
|
|
|
disableUnavailableVehicleTypes(self);
|
|
self->invalidate();
|
|
|
|
if (self->width < 220)
|
|
self->width = 220;
|
|
|
|
self->row_count = 0;
|
|
refreshVehicleList(self);
|
|
|
|
self->var_83C = 0;
|
|
self->row_hover = -1;
|
|
|
|
self->callOnResize();
|
|
self->callOnPeriodicUpdate();
|
|
self->callPrepareDraw();
|
|
self->initScrollWidgets();
|
|
self->invalidate();
|
|
self->moveInsideScreenEdges();
|
|
}
|
|
|
|
// 0x004C2409
|
|
static void onMouseUp(Window* self, WidgetIndex_t widgetIndex)
|
|
{
|
|
switch (widgetIndex)
|
|
{
|
|
case Widx::close_button:
|
|
WindowManager::close(self);
|
|
break;
|
|
|
|
case Widx::tab_trains:
|
|
case Widx::tab_buses:
|
|
case Widx::tab_trucks:
|
|
case Widx::tab_trams:
|
|
case Widx::tab_aircraft:
|
|
case Widx::tab_ships:
|
|
{
|
|
auto vehicleType = VehicleType(widgetIndex - Widx::tab_trains);
|
|
switchTab(self, vehicleType);
|
|
break;
|
|
}
|
|
|
|
case Widx::sort_name:
|
|
case Widx::sort_profit:
|
|
case Widx::sort_age:
|
|
case Widx::sort_reliability:
|
|
{
|
|
auto sortMode = widgetIndex - Widx::sort_name;
|
|
if (self->sort_mode == sortMode)
|
|
return;
|
|
|
|
self->sort_mode = sortMode;
|
|
self->invalidate();
|
|
refreshVehicleList(self);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 0x004C2434
|
|
static void onMouseDown(Window* self, WidgetIndex_t widgetIndex)
|
|
{
|
|
if (widgetIndex == Widx::company_select)
|
|
Dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
|
|
|
|
else if (widgetIndex == Widx::filter_type_btn)
|
|
{
|
|
Widget dropdown = self->widgets[Widx::filter_type];
|
|
Dropdown::show(self->x + dropdown.left, self->y + dropdown.top, dropdown.width() - 4, dropdown.height(), self->getColour(WindowColour::secondary), 3, 0x80);
|
|
|
|
Dropdown::add(0, StringIds::dropdown_stringid, StringIds::all_vehicles);
|
|
Dropdown::add(1, StringIds::dropdown_stringid, StringIds::stopping_at_station);
|
|
Dropdown::add(2, StringIds::dropdown_stringid, StringIds::transporting_cargo);
|
|
Dropdown::setItemSelected(self->var_88A);
|
|
}
|
|
else if (widgetIndex == Widx::cargo_type_btn)
|
|
{
|
|
auto index = 0;
|
|
auto selectedIndex = -1;
|
|
for (uint16_t cargoId = 0; cargoId < ObjectManager::getMaxObjects(ObjectType::cargo); ++cargoId)
|
|
{
|
|
auto cargoObj = ObjectManager::get<CargoObject>(cargoId);
|
|
if (cargoObj == nullptr)
|
|
continue;
|
|
|
|
FormatArguments args{};
|
|
args.push(cargoObj->name);
|
|
args.push(cargoObj->unit_inline_sprite);
|
|
args.push(cargoId);
|
|
Dropdown::add(index, StringIds::carrying_cargoid_sprite, args);
|
|
|
|
if (index == self->var_88C)
|
|
selectedIndex = index;
|
|
|
|
index++;
|
|
}
|
|
|
|
Widget dropdown = self->widgets[Widx::cargo_type];
|
|
Dropdown::showText(self->x + dropdown.left, self->y + dropdown.top, dropdown.width() - 4, dropdown.height(), self->getColour(WindowColour::secondary), index, 0);
|
|
if (selectedIndex != -1)
|
|
Dropdown::setItemSelected(selectedIndex);
|
|
}
|
|
}
|
|
|
|
// 0x004C243F
|
|
static void onCompanyDropdown(Ui::Window* self, int16_t itemIndex)
|
|
{
|
|
if (itemIndex == -1)
|
|
return;
|
|
|
|
CompanyId_t companyId = Dropdown::getCompanyIdFromSelection(itemIndex);
|
|
|
|
// Try to find an open vehicle list for this company.
|
|
auto companyWindow = WindowManager::bringToFront(WindowType::vehicleList, companyId);
|
|
if (companyWindow != nullptr)
|
|
return;
|
|
|
|
// If not, we'll turn this window into a window for the company selected.
|
|
auto company = CompanyManager::get(companyId);
|
|
if (company->name == StringIds::empty)
|
|
return;
|
|
|
|
self->number = companyId;
|
|
self->owner = companyId;
|
|
|
|
disableUnavailableVehicleTypes(self);
|
|
|
|
self->row_count = 0;
|
|
refreshVehicleList(self);
|
|
|
|
self->var_83C = 0;
|
|
self->row_hover = -1;
|
|
|
|
self->callOnResize();
|
|
self->callPrepareDraw();
|
|
self->initScrollWidgets();
|
|
self->invalidate();
|
|
}
|
|
|
|
static void onDropdown(Ui::Window* self, WidgetIndex_t widgetIndex, int16_t itemIndex)
|
|
{
|
|
if (widgetIndex == Widx::company_select)
|
|
return onCompanyDropdown(self, itemIndex);
|
|
|
|
if (widgetIndex == filter_type_btn && itemIndex != -1)
|
|
{
|
|
if (self->var_88A != itemIndex)
|
|
{
|
|
self->var_88A = itemIndex;
|
|
self->var_88C = -1;
|
|
}
|
|
}
|
|
|
|
else if (widgetIndex == cargo_type_btn && itemIndex != -1)
|
|
{
|
|
self->var_88C = Dropdown::getItemArgument(itemIndex, 3);
|
|
}
|
|
}
|
|
|
|
// 0x004C24CA
|
|
static std::optional<FormatArguments> tooltip(Window* self, WidgetIndex_t widgetIndex)
|
|
{
|
|
FormatArguments args{};
|
|
args.push(StringIds::tooltip_scroll_vehicle_list);
|
|
return args;
|
|
}
|
|
|
|
// 0x004C260B
|
|
static void onUpdate(Window* self)
|
|
{
|
|
self->frame_no++;
|
|
self->callPrepareDraw();
|
|
|
|
auto widgetIndex = getTabFromType(static_cast<VehicleType>(self->current_tab));
|
|
WindowManager::invalidateWidget(WindowType::vehicleList, self->number, widgetIndex);
|
|
|
|
updateVehicleList(self);
|
|
updateVehicleList(self);
|
|
updateVehicleList(self);
|
|
|
|
self->invalidate();
|
|
}
|
|
|
|
// 0x004C2640
|
|
static void event_08(Window* self)
|
|
{
|
|
self->flags |= WindowFlags::not_scroll_view;
|
|
}
|
|
|
|
// 0x004C2648
|
|
static void event_09(Window* self)
|
|
{
|
|
if (self->flags & WindowFlags::not_scroll_view)
|
|
{
|
|
self->row_hover = -1;
|
|
}
|
|
}
|
|
|
|
// 0x004C265B
|
|
static void getScrollSize(Window* self, uint32_t scrollIndex, uint16_t* scrollWidth, uint16_t* scrollHeight)
|
|
{
|
|
*scrollHeight = self->var_83C * self->row_height;
|
|
}
|
|
|
|
// 0x004C266D
|
|
static CursorId cursor(Window* self, int16_t widgetIdx, int16_t xPos, int16_t yPos, CursorId fallback)
|
|
{
|
|
if (widgetIdx != Widx::scrollview)
|
|
return fallback;
|
|
|
|
uint16_t currentIndex = yPos / self->row_height;
|
|
if (currentIndex < self->var_83C && self->row_info[currentIndex] != -1)
|
|
return CursorId::handPointer;
|
|
|
|
return fallback;
|
|
}
|
|
|
|
// 0x004C26A4
|
|
static void onScrollMouseOver(Window* self, int16_t x, int16_t y, uint8_t scroll_index)
|
|
{
|
|
Input::setTooltipTimeout(2000);
|
|
|
|
self->flags &= ~WindowFlags::not_scroll_view;
|
|
|
|
uint16_t currentRow = y / self->row_height;
|
|
if (currentRow < self->var_83C)
|
|
self->row_hover = self->row_info[currentRow];
|
|
else
|
|
self->row_hover = -1;
|
|
|
|
string_id tooltipId = StringIds::buffer_337;
|
|
if (self->row_hover == -1)
|
|
tooltipId = StringIds::null;
|
|
|
|
char* tooltipBuffer = const_cast<char*>(StringManager::getString(StringIds::buffer_337));
|
|
|
|
// Have we already got the right tooltip?
|
|
if (tooltipBuffer[0] != '\0' && self->widgets[Widx::scrollview].tooltip == tooltipId && self->row_hover == self->var_85C)
|
|
return;
|
|
|
|
self->widgets[Widx::scrollview].tooltip = tooltipId;
|
|
self->var_85C = self->row_hover;
|
|
Ui::Windows::ToolTip::closeAndReset();
|
|
|
|
if (self->row_hover == -1)
|
|
return;
|
|
|
|
// Initialise tooltip buffer.
|
|
char* buffer = StringManager::formatString(tooltipBuffer, StringIds::vehicle_list_tooltip_load);
|
|
|
|
// Append load to buffer.
|
|
auto head = EntityManager::get<VehicleHead>(self->var_85C);
|
|
buffer = head->generateCargoTotalString(buffer);
|
|
|
|
// Figure out what stations the vehicle stops at.
|
|
auto orders = Vehicles::OrderRingView(head->orderTableOffset);
|
|
bool isFirstStop = true;
|
|
for (auto& order : orders)
|
|
{
|
|
// Is this order a station?
|
|
auto* stopOrder = order.as<Vehicles::OrderStopAt>();
|
|
if (stopOrder == nullptr)
|
|
continue;
|
|
|
|
string_id stopFormat = StringIds::vehicle_list_tooltip_comma_stringid;
|
|
if (isFirstStop)
|
|
stopFormat = StringIds::vehicle_list_tooltip_stops_at_stringid;
|
|
|
|
// Append station name to the tooltip buffer
|
|
auto args = FormatArguments::common();
|
|
stopOrder->setFormatArguments(args);
|
|
buffer = StringManager::formatString(buffer, stopFormat, &args);
|
|
|
|
isFirstStop = false;
|
|
}
|
|
}
|
|
|
|
// 0x004C27C0
|
|
static void onScrollMouseDown(Window* self, int16_t x, int16_t y, uint8_t scroll_index)
|
|
{
|
|
uint16_t currentRow = y / self->row_height;
|
|
if (currentRow >= self->var_83C)
|
|
return;
|
|
|
|
int16_t currentVehicleId = self->row_info[currentRow];
|
|
if (currentVehicleId == -1)
|
|
return;
|
|
|
|
auto head = EntityManager::get<VehicleHead>(currentVehicleId);
|
|
if (head->isPlaced())
|
|
Ui::Windows::Vehicle::Main::open(head);
|
|
else
|
|
Ui::Windows::Vehicle::Details::open(head);
|
|
}
|
|
|
|
// 0x004C2820
|
|
static void onResize(Window* self)
|
|
{
|
|
self->flags |= WindowFlags::resizable;
|
|
|
|
self->min_width = min_dimensions.width;
|
|
self->min_height = min_dimensions.height;
|
|
|
|
self->max_width = max_dimensions.width;
|
|
self->max_height = max_dimensions.height;
|
|
|
|
if (self->width < self->min_width)
|
|
{
|
|
self->width = self->min_width;
|
|
self->invalidate();
|
|
}
|
|
|
|
if (self->height < self->min_height)
|
|
{
|
|
self->height = self->min_height;
|
|
self->invalidate();
|
|
}
|
|
}
|
|
|
|
static void initEvents()
|
|
{
|
|
_events.prepare_draw = prepareDraw;
|
|
_events.draw = draw;
|
|
_events.draw_scroll = drawScroll;
|
|
_events.on_mouse_up = onMouseUp;
|
|
_events.on_mouse_down = onMouseDown;
|
|
_events.on_dropdown = onDropdown;
|
|
_events.tooltip = tooltip;
|
|
_events.on_update = onUpdate;
|
|
_events.event_08 = event_08;
|
|
_events.event_09 = event_09;
|
|
_events.get_scroll_size = getScrollSize;
|
|
_events.cursor = cursor;
|
|
_events.scroll_mouse_down = onScrollMouseDown;
|
|
_events.scroll_mouse_over = onScrollMouseOver;
|
|
_events.on_resize = onResize;
|
|
}
|
|
}
|