diff --git a/src/order_func.h b/src/order_func.h index ff7d865f1a..70ec576445 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -10,6 +10,7 @@ #ifndef ORDER_FUNC_H #define ORDER_FUNC_H +#include "core/geometry_type.hpp" #include "order_type.h" #include "vehicle_type.h" #include "company_type.h" @@ -24,7 +25,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v); uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth = 0); -void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_index, int y, bool selected, bool timetable, int left, int middle, int right); +void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_index, bool selected, bool timetable, Rect index_rect, Rect order_rect, int rating_width); static const uint DEF_SERVINT_DAYS_TRAINS = 150; static const uint DEF_SERVINT_DAYS_ROADVEH = 150; diff --git a/src/order_gui.cpp b/src/order_gui.cpp index b09f97588d..0bdfd979d1 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -22,6 +22,7 @@ #include "tilehighlight_func.h" #include "network/network.h" #include "station_base.h" +#include "station_gui.h" #include "industry.h" #include "waypoint_base.h" #include "core/geometry_func.hpp" @@ -33,6 +34,7 @@ #include "vehicle_func.h" #include "error.h" #include "order_cmd.h" +#include "order_func.h" #include "company_cmd.h" #include "core/string_consumer.hpp" @@ -218,14 +220,13 @@ static StringID GetOrderGoToString(const Order &order) * @param v Vehicle the order belongs to * @param order The order to draw * @param order_index Index of the order in the orders of the vehicle - * @param y Y position for drawing * @param selected True, if the order is selected * @param timetable True, when drawing in the timetable GUI - * @param left Left border for text drawing - * @param middle X position between order index and order text - * @param right Right border for text drawing + * @param index_rect Rect to draw order index, and current order marker. + * @param order_rect Rect to draw order detail text within. + * @param rating_width Width of each station rating mini graph, or 0 to disable drawing ratings. */ -void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_index, int y, bool selected, bool timetable, int left, int middle, int right) +void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_index, bool selected, bool timetable, Rect index_rect, Rect order_rect, int rating_width) { bool rtl = _current_text_dir == TD_RTL; @@ -233,11 +234,11 @@ void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_ Dimension sprite_size = GetSpriteSize(sprite); if (v->cur_real_order_index == order_index) { /* Draw two arrows before the next real order. */ - DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)GetCharacterHeight(FS_NORMAL) - (int)sprite_size.height) / 2); - DrawSprite(sprite, PAL_NONE, rtl ? right - 2 * sprite_size.width : left + sprite_size.width, y + ((int)GetCharacterHeight(FS_NORMAL) - (int)sprite_size.height) / 2); + DrawSpriteIgnorePadding(sprite, PAL_NONE, index_rect.WithWidth(sprite_size.width, rtl), SA_CENTER); + DrawSpriteIgnorePadding(sprite, PAL_NONE, index_rect.Indent(sprite_size.width, rtl).WithWidth(sprite_size.width, rtl), SA_CENTER); } else if (v->cur_implicit_order_index == order_index) { /* Draw one arrow before the next implicit order; the next real order will still get two arrows. */ - DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)GetCharacterHeight(FS_NORMAL) - (int)sprite_size.height) / 2); + DrawSpriteIgnorePadding(sprite, PAL_NONE, index_rect.WithWidth(sprite_size.width, rtl), SA_CENTER); } TextColour colour = TC_BLACK; @@ -247,8 +248,9 @@ void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_ colour = TC_WHITE; } - DrawString(left, rtl ? right - 2 * sprite_size.width - 3 : middle, y, GetString(STR_ORDER_INDEX, order_index + 1), colour, SA_RIGHT | SA_FORCE); + DrawString(index_rect.Indent(sprite_size.width * 2, rtl), GetString(STR_ORDER_INDEX, order_index + 1), colour, SA_RIGHT | SA_FORCE); + const Station *st = nullptr; std::string line; switch (order->GetType()) { @@ -264,7 +266,8 @@ void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_ case OT_GOTO_STATION: { OrderLoadFlags load = order->GetLoadType(); OrderUnloadFlags unload = order->GetUnloadType(); - bool valid_station = CanVehicleUseStation(v, Station::Get(order->GetDestination().ToStationID())); + st = Station::Get(order->GetDestination().ToStationID()); + bool valid_station = CanVehicleUseStation(v, st); line = GetString(valid_station ? STR_ORDER_GO_TO_STATION : STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION, STR_ORDER_GO_TO + (v->IsGroundVehicle() ? order->GetNonStopType() : 0), order->GetDestination()); if (timetable) { @@ -360,7 +363,12 @@ void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_ } } - DrawString(rtl ? left : middle, rtl ? middle : right, y, line, colour); + int w = GetStringBoundingBox(line).width; + DrawString(order_rect, line, colour); + + if (st != nullptr && rating_width > 0) { + DrawStationRatingMiniGraphs(st, order_rect.Indent(w + WidgetDimensions::scaled.hsep_wide, rtl), rating_width); + } } /** @@ -561,6 +569,7 @@ private: DP_BOTTOM_MIDDLE_STOP_SHARING = 1, ///< Display 'stop sharing' in the middle button of the bottom row of the vehicle order window. }; + int rating_width = 0; int selected_order = -1; VehicleOrderID order_over = INVALID_VEH_ORDER_ID; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none. OrderPlaceObjectState goto_type = OPOS_NONE; @@ -818,6 +827,11 @@ public: this->OnInvalidateData(VIWD_MODIFY_ORDERS); } + void OnInit() override + { + this->rating_width = GetStationRatingMiniGraphWidth(); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { @@ -1101,10 +1115,8 @@ public: bool rtl = _current_text_dir == TD_RTL; uint64_t max_value = GetParamMaxValue(this->vehicle->GetNumOrders(), 2); int index_column_width = GetStringBoundingBox(GetString(STR_ORDER_INDEX, max_value)).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + WidgetDimensions::scaled.hsep_normal; - int middle = rtl ? ir.right - index_column_width : ir.left + index_column_width; - int y = ir.top; - int line_height = this->GetWidget(WID_O_ORDER_LIST)->resize_y; + Rect tr = ir.WithHeight(this->GetWidget(WID_O_ORDER_LIST)->resize_y); VehicleOrderID i = this->vscroll->GetPosition(); VehicleOrderID num_orders = this->vehicle->GetNumOrders(); @@ -1117,19 +1129,18 @@ public: if (i != this->selected_order && i == this->order_over) { /* Highlight dragged order destination. */ - int top = (this->order_over < this->selected_order ? y : y + line_height) - WidgetDimensions::scaled.framerect.top; + int top = (this->order_over < this->selected_order ? tr.top : tr.bottom) - WidgetDimensions::scaled.framerect.top; int bottom = std::min(top + 2, ir.bottom); top = std::max(top - 3, ir.top); GfxFillRect(ir.left, top, ir.right, bottom, GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST)); break; } - y += line_height; - - i++; + tr = tr.Translate(0, tr.Height()); + ++i; } /* Reset counters for drawing the orders. */ - y = ir.top; + tr = ir; i = this->vscroll->GetPosition(); } @@ -1138,15 +1149,14 @@ public: /* Don't draw anything if it extends past the end of the window. */ if (!this->vscroll->IsVisible(i)) break; - DrawOrderString(this->vehicle, this->vehicle->GetOrder(i), i, y, i == this->selected_order, false, ir.left, middle, ir.right); - y += line_height; - - i++; + DrawOrderString(this->vehicle, this->vehicle->GetOrder(i), i, i == this->selected_order, false, tr.WithWidth(index_column_width, rtl), tr.Indent(index_column_width, rtl), this->rating_width); + tr = tr.Translate(0, tr.Height()); + ++i; } if (this->vscroll->IsVisible(i)) { StringID str = this->vehicle->IsOrderListShared() ? STR_ORDERS_END_OF_SHARED_ORDERS : STR_ORDERS_END_OF_ORDERS; - DrawString(rtl ? ir.left : middle, rtl ? middle : ir.right, y, str, (i == this->selected_order) ? TC_WHITE : TC_BLACK); + DrawString(tr.Indent(index_column_width, rtl), str, (i == this->selected_order) ? TC_WHITE : TC_BLACK); } } diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 4f0979ec86..d50e152ad2 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -203,53 +203,92 @@ void CheckRedrawRoadWaypointCoverage(const Window *) CheckRedrawWaypointCoverage(); } +/** + * Get width required for station rating mini graphs + * @return width of mini graphs. + */ +uint GetStationRatingMiniGraphWidth() +{ + /* Determine appropriate width for mini station rating graph */ + uint rating_width = 0; + for (const CargoSpec *cs : _sorted_standard_cargo_specs) { + rating_width = std::max(rating_width, GetStringBoundingBox(cs->abbrev, FS_SMALL).width); + } + /* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */ + return rating_width * 16 / 10; +} + /** * Draw small boxes of cargo amount and ratings data at the given * coordinates. If amount exceeds 576 units, it is shown 'full', same * goes for the rating: at above 90% orso (224) it is also 'full' - * - * @param left left most coordinate to draw the box at - * @param right right most coordinate to draw the box at - * @param y coordinate to draw the box at + * @param r Rect to draw mini graph in. * @param cargo Cargo type * @param amount Cargo amount * @param rating ratings data for that particular cargo */ -static void StationsWndShowStationRating(int left, int right, int y, CargoType cargo, uint amount, uint8_t rating) +static void DrawStationRatingMiniGraph(const Rect &r, CargoType cargo_type, uint amount, uint8_t rating) { - static const uint units_full = 576; ///< number of units to show station as 'full' - static const uint rating_full = 224; ///< rating needed so it is shown as 'full' + static constexpr uint units_full = 576; ///< number of units to show station as 'full' + static constexpr uint rating_full = 224; ///< rating needed so it is shown as 'full' - const CargoSpec *cs = CargoSpec::Get(cargo); + bool rtl = _current_text_dir == TD_RTL; + + const CargoSpec *cs = CargoSpec::Get(cargo_type); if (!cs->IsValid()) return; - int padding = ScaleGUITrad(1); - int width = right - left; int colour = cs->rating_colour; TextColour tc = GetContrastColour(colour); - uint w = std::min(amount + 5, units_full) * width / units_full; + uint w = std::min(amount + 5, units_full) * r.Width() / units_full; - int height = GetCharacterHeight(FS_SMALL) + padding - 1; + int height = GetCharacterHeight(FS_SMALL) + ScaleGUITrad(1); + Rect gr = r.WithHeight(height); if (amount > 30) { /* Draw total cargo (limited) on station */ - GfxFillRect(left, y, left + w - 1, y + height, colour); + GfxFillRect(gr.WithWidth(w, rtl), colour); } else { /* Draw a (scaled) one pixel-wide bar of additional cargo meter, useful * for stations with only a small amount (<=30) */ uint rest = ScaleGUITrad(amount) / 5; if (rest != 0) { - GfxFillRect(left, y + height - rest, left + padding - 1, y + height, colour); + GfxFillRect(gr.WithWidth(ScaleGUITrad(1), rtl).WithHeight(rest, true), colour); } } - DrawString(left + padding, right, y, cs->abbrev, tc, SA_CENTER, false, FS_SMALL); + DrawString(r, cs->abbrev, tc, SA_CENTER, false, FS_SMALL); /* Draw green/red ratings bar (fits under the waiting bar) */ - y += height + padding + 1; - GfxFillRect(left + padding, y, right - padding - 1, y + padding - 1, PC_RED); - w = std::min(rating, rating_full) * (width - padding - padding) / rating_full; - if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN); + Rect br = r.Shrink(ScaleGUITrad(1)).WithHeight(ScaleGUITrad(1), true); + GfxFillRect(br, PC_RED); + + w = std::min(rating, rating_full) * br.Width() / rating_full; + if (w != 0) GfxFillRect(br.WithWidth(w, rtl), PC_GREEN); +} + +/** + * Draw all station rating mini graphs. + * @param st Station to draw graphs for. + * @param row Rect to draw row of graphs within. + * @param rating_width Width of each rating grpah. + */ +void DrawStationRatingMiniGraphs(const Station *st, const Rect &row, uint rating_width) +{ + bool rtl = _current_text_dir == TD_RTL; + Rect r = row.WithWidth(rating_width, rtl); + int delta_x = rating_width + WidgetDimensions::scaled.hsep_normal; + if (rtl) delta_x = -delta_x; + + /* Draw cargo waiting and station ratings */ + for (const CargoSpec *cs : _sorted_standard_cargo_specs) { + const GoodsEntry &ge = st->goods[cs->Index()]; + if (!ge.HasRating()) continue; + if (r.left < row.left || r.right > row.right) break; + + DrawStationRatingMiniGraph(r, cs->Index(), ge.HasData() ? ge.GetData().cargo.TotalCount() : 0, ge.rating); + + r = r.Translate(delta_x, 0); + } } typedef GUIList GUIStationList; @@ -449,6 +488,11 @@ public: CompanyStationsWindow::initial_state = this->filter; } + void OnInit() override + { + this->rating_width = GetStationRatingMiniGraphWidth(); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { @@ -471,14 +515,6 @@ public: case WID_STL_LIST: fill.height = resize.height = std::max(GetCharacterHeight(FS_NORMAL), GetCharacterHeight(FS_SMALL) + ScaleGUITrad(3)); size.height = padding.height + 5 * resize.height; - - /* Determine appropriate width for mini station rating graph */ - this->rating_width = 0; - for (const CargoSpec *cs : _sorted_standard_cargo_specs) { - this->rating_width = std::max(this->rating_width, GetStringBoundingBox(cs->abbrev, FS_SMALL).width); - } - /* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */ - this->rating_width = this->rating_width * 16 / 10; break; } } @@ -501,12 +537,7 @@ public: case WID_STL_LIST: { bool rtl = _current_text_dir == TD_RTL; - Rect tr = r.Shrink(WidgetDimensions::scaled.framerect); - uint line_height = this->GetWidget(widget)->resize_y; - /* Spacing between station name and first rating graph. */ - int text_spacing = WidgetDimensions::scaled.hsep_wide; - /* Spacing between additional rating graphs. */ - int rating_spacing = WidgetDimensions::scaled.hsep_normal; + Rect tr = r.Shrink(WidgetDimensions::scaled.framerect).WithHeight(this->GetWidget(widget)->resize_y); auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->stations); for (auto it = first; it != last; ++it) { @@ -517,33 +548,16 @@ public: * when the order had been removed and the station list hasn't been removed yet */ assert(st->owner == owner || st->owner == OWNER_NONE); - int x = DrawString(tr.left, tr.right, tr.top + (line_height - GetCharacterHeight(FS_NORMAL)) / 2, GetString(STR_STATION_LIST_STATION, st->index, st->facilities)); - x += rtl ? -text_spacing : text_spacing; + std::string str = GetString(STR_STATION_LIST_STATION, st->index, st->facilities); + int w = GetStringBoundingBox(str).width; + DrawString(tr.left, tr.right, tr.top + (tr.Height() - GetCharacterHeight(FS_NORMAL)) / 2, str); - /* show cargo waiting and station ratings */ - for (const CargoSpec *cs : _sorted_standard_cargo_specs) { - CargoType cargo_type = cs->Index(); - if (st->goods[cargo_type].HasRating()) { - /* For RTL we work in exactly the opposite direction. So - * decrement the space needed first, then draw to the left - * instead of drawing to the left and then incrementing - * the space. */ - if (rtl) { - x -= rating_width + rating_spacing; - if (x < tr.left) break; - } - StationsWndShowStationRating(x, x + rating_width, tr.top, cargo_type, st->goods[cargo_type].HasData() ? st->goods[cargo_type].GetData().cargo.TotalCount() : 0, st->goods[cargo_type].rating); - if (!rtl) { - x += rating_width + rating_spacing; - if (x > tr.right) break; - } - } - } - tr.top += line_height; + DrawStationRatingMiniGraphs(st, tr.Indent(w + WidgetDimensions::scaled.hsep_wide, rtl), this->rating_width); + tr = tr.Translate(0, tr.Height()); } if (this->vscroll->GetCount() == 0) { // company has no stations - DrawString(tr.left, tr.right, tr.top + (line_height - GetCharacterHeight(FS_NORMAL)) / 2, STR_STATION_LIST_NONE); + DrawString(tr.left, tr.right, tr.top + (tr.Height() - GetCharacterHeight(FS_NORMAL)) / 2, STR_STATION_LIST_NONE); return; } break; diff --git a/src/station_gui.h b/src/station_gui.h index 39a98cc729..bdc7b288b1 100644 --- a/src/station_gui.h +++ b/src/station_gui.h @@ -35,4 +35,7 @@ void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc); void ShowSelectRailWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc); void ShowSelectRoadWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc); +uint GetStationRatingMiniGraphWidth(); +void DrawStationRatingMiniGraphs(const Station *st, const Rect &row, uint rating_width); + #endif /* STATION_GUI_H */ diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 033555b6a1..30c46aed01 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -427,7 +427,7 @@ struct TimetableWindow : Window { void DrawTimetablePanel(const Rect &r) const { const Vehicle *v = this->vehicle; - Rect tr = r.Shrink(WidgetDimensions::scaled.framerect); + Rect tr = r.Shrink(WidgetDimensions::scaled.framerect).WithHeight(GetCharacterHeight(FS_NORMAL)); int i = this->vscroll->GetPosition(); VehicleOrderID order_id = (i + 1) / 2; bool final_order = false; @@ -435,7 +435,6 @@ struct TimetableWindow : Window { bool rtl = _current_text_dir == TD_RTL; int index_column_width = GetStringBoundingBox(GetString(STR_ORDER_INDEX, GetParamMaxValue(v->GetNumOrders(), 2))).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + WidgetDimensions::scaled.hsep_normal; - int middle = rtl ? tr.right - index_column_width : tr.left + index_column_width; auto orders = v->Orders(); while (true) { @@ -443,20 +442,20 @@ struct TimetableWindow : Window { if (!this->vscroll->IsVisible(i)) break; if (i % 2 == 0) { - DrawOrderString(v, &orders[order_id], order_id, tr.top, i == selected, true, tr.left, middle, tr.right); + DrawOrderString(v, &orders[order_id], order_id, i == selected, true, tr.WithWidth(index_column_width, rtl), tr.Indent(index_column_width, rtl), 0); if (order_id > v->orders->GetNext(order_id)) final_order = true; order_id = v->orders->GetNext(order_id); } else { TextColour colour; std::string string = GetTimetableTravelString(orders[order_id], i, colour); - DrawString(rtl ? tr.left : middle, rtl ? middle : tr.right, tr.top, string, colour); + DrawString(tr.Indent(index_column_width, rtl), string, colour); if (final_order) break; } i++; - tr.top += GetCharacterHeight(FS_NORMAL); + tr = tr.Translate(0, tr.Height()); } }