diff --git a/src/lang/english.txt b/src/lang/english.txt index 4064c75143..56a1c9df63 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2617,6 +2617,11 @@ STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLA STR_LINKGRAPH_LEGEND_SATURATED :{TINY_FONT}{BLACK}saturated STR_LINKGRAPH_LEGEND_OVERLOADED :{TINY_FONT}{BLACK}overloaded +# Linkgraph tooltip +STR_LINKGRAPH_STATS_TOOLTIP :{BLACK}{CARGO_LONG} to be transported per month from {STATION} to {STATION} ({COMMA}% of capacity){RAW_STRING} +STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION :{}{CARGO_LONG} to be transported back ({COMMA}% of capacity) +STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION :{}Average travel time: {NUM}{NBSP}day{P "" s} + # Base for station construction window(s) STR_STATION_BUILD_COVERAGE_AREA_TITLE :{BLACK}Coverage area highlight STR_STATION_BUILD_COVERAGE_OFF :{BLACK}Off diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index f1fb309c1f..c32c5d395a 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -219,8 +219,10 @@ void LinkGraphOverlay::AddLinks(const Station *from, const Station *to) const LinkGraph &lg = *LinkGraph::Get(ge.link_graph); ConstEdge edge = lg[ge.node][to->goods[c].node]; if (edge.Capacity() > 0) { - this->AddStats(lg.Monthly(edge.Capacity()), lg.Monthly(edge.Usage()), - ge.flows.GetFlowVia(to->index), from->owner == OWNER_NONE || to->owner == OWNER_NONE, + this->AddStats(c, lg.Monthly(edge.Capacity()), lg.Monthly(edge.Usage()), + ge.flows.GetFlowVia(to->index), + edge.TravelTime() / DAY_TICKS, + from->owner == OWNER_NONE || to->owner == OWNER_NONE, this->cached_links[from->index][to->index]); } } @@ -236,14 +238,16 @@ void LinkGraphOverlay::AddLinks(const Station *from, const Station *to) * @param new_shared If the new link is shared. * @param cargo LinkProperties to write the information to. */ -/* static */ void LinkGraphOverlay::AddStats(uint new_cap, uint new_usg, uint new_plan, bool new_shared, LinkProperties &cargo) +/* static */ void LinkGraphOverlay::AddStats(CargoID new_cargo, uint new_cap, uint new_usg, uint new_plan, uint32 time, bool new_shared, LinkProperties &cargo) { /* multiply the numbers by 32 in order to avoid comparing to 0 too often. */ if (cargo.capacity == 0 || - std::max(cargo.usage, cargo.planned) * 32 / (cargo.capacity + 1) < std::max(new_usg, new_plan) * 32 / (new_cap + 1)) { + cargo.Usage() * 32 / (cargo.capacity + 1) < std::max(new_usg, new_plan) * 32 / (new_cap + 1)) { + cargo.cargo = new_cargo; cargo.capacity = new_cap; cargo.usage = new_usg; cargo.planned = new_plan; + cargo.time = time; } if (new_shared) cargo.shared = true; } @@ -289,7 +293,7 @@ void LinkGraphOverlay::DrawLinks(const DrawPixelInfo *dpi) const */ void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const { - uint usage_or_plan = std::min(cargo.capacity * 2 + 1, std::max(cargo.usage, cargo.planned)); + uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage()); int colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][usage_or_plan * lengthof(LinkGraphOverlay::LINK_COLOURS[0]) / (cargo.capacity * 2 + 2)]; int width = ScaleGUITrad(this->scale); int dash = cargo.shared ? width * 4 : 0; @@ -354,6 +358,56 @@ void LinkGraphOverlay::DrawStationDots(const DrawPixelInfo *dpi) const GfxDrawLine(x + w2, y - w1, x + w2, y + w2, border_colour); } +bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) +{ + for (auto i(this->cached_links.crbegin()); i != this->cached_links.crend(); ++i) { + if (!Station::IsValidID(i->first)) continue; + Point pta = this->GetStationMiddle(Station::Get(i->first)); + for (auto j(i->second.crbegin()); j != i->second.crend(); ++j) { + if (!Station::IsValidID(j->first)) continue; + if (i->first == j->first) continue; + + /* Check the distance from the cursor to the line defined by the two stations. */ + Point ptb = this->GetStationMiddle(Station::Get(j->first)); + float dist = std::abs((ptb.x - pta.x) * (pta.y - pt.y) - (pta.x - pt.x) * (ptb.y - pta.y)) / + std::sqrt((ptb.x - pta.x) * (ptb.x - pta.x) + (ptb.y - pta.y) * (ptb.y - pta.y)); + const auto &link = j->second; + if (dist <= 4 && link.Usage() > 0 && + pt.x >= std::min(pta.x, ptb.x) && + pt.x <= std::max(pta.x, ptb.x)) { + static char buf[1024]; + char *buf_end = buf; + buf[0] = 0; + /* Fill buf with more information if this is a bidirectional link. */ + auto k = this->cached_links[j->first].find(i->first); + const auto &back = k->second; + if (k != this->cached_links[j->first].end() && back.Usage() > 0) { + SetDParam(0, back.cargo); + SetDParam(1, back.Usage()); + SetDParam(2, back.Usage() * 100 / (back.capacity + 1)); + buf_end = GetString(buf, STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION, lastof(buf)); + } + /* Add information about the travel time if known. */ + const auto time = link.time ? back.time ? ((link.time + back.time) / 2) : link.time : back.time; + if (time > 0) { + SetDParam(0, time); + buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION, lastof(buf)); + } + SetDParam(0, link.cargo); + SetDParam(1, link.Usage()); + SetDParam(2, i->first); + SetDParam(3, j->first); + SetDParam(4, link.Usage() * 100 / (link.capacity + 1)); + SetDParamStr(5, buf); + GuiShowTooltips(this->window, STR_LINKGRAPH_STATS_TOOLTIP, 7, nullptr, close_cond); + return true; + } + } + } + GuiShowTooltips(this->window, STR_NULL, 0, nullptr, close_cond); + return false; +} + /** * Determine the middle of a station in the current window. * @param st The station we're looking for. diff --git a/src/linkgraph/linkgraph_gui.h b/src/linkgraph/linkgraph_gui.h index e6b1114008..ef249b9bfd 100644 --- a/src/linkgraph/linkgraph_gui.h +++ b/src/linkgraph/linkgraph_gui.h @@ -19,14 +19,20 @@ #include /** - * Properties of a link between two stations. + * Monthly statistics for a link between two stations. + * Only the cargo type of the most saturated linkgraph is taken into account. */ struct LinkProperties { - LinkProperties() : capacity(0), usage(0), planned(0), shared(false) {} + LinkProperties() : cargo(CT_INVALID), capacity(0), usage(0), planned(0), shared(false) {} + /** Return the usage of the link to display. */ + uint Usage() const { return std::max(this->usage, this->planned); } + + CargoID cargo; ///< Cargo type of the link. uint capacity; ///< Capacity of the link. uint usage; ///< Actual usage of the link. uint planned; ///< Planned usage of the link. + uint32 time; ///< Travel time of the link. bool shared; ///< If this is a shared link to be drawn dashed. }; @@ -50,7 +56,7 @@ public: * @param company_mask Bitmask of companies to be shown. * @param scale Desired thickness of lines and size of station dots. */ - LinkGraphOverlay(const Window *w, uint wid, CargoTypes cargo_mask, uint32 company_mask, uint scale) : + LinkGraphOverlay(Window *w, uint wid, CargoTypes cargo_mask, uint32 company_mask, uint scale) : window(w), widget_id(wid), cargo_mask(cargo_mask), company_mask(company_mask), scale(scale) {} @@ -58,6 +64,8 @@ public: void SetCargoMask(CargoTypes cargo_mask); void SetCompanyMask(uint32 company_mask); + bool ShowTooltip(Point pt, TooltipCloseCondition close_cond); + /** Mark the linkgraph dirty to be rebuilt next time Draw() is called. */ void SetDirty() { this->dirty = true; } @@ -68,7 +76,7 @@ public: uint32 GetCompanyMask() { return this->company_mask; } protected: - const Window *window; ///< Window to be drawn into. + Window *window; ///< Window to be drawn into. const uint widget_id; ///< ID of Widget in Window to be drawn to. CargoTypes cargo_mask; ///< Bitmask of cargos to be displayed. uint32 company_mask; ///< Bitmask of companies to be displayed. @@ -88,7 +96,7 @@ protected: void GetWidgetDpi(DrawPixelInfo *dpi) const; void RebuildCache(); - static void AddStats(uint new_cap, uint new_usg, uint new_flow, bool new_shared, LinkProperties &cargo); + static void AddStats(CargoID new_cargo, uint new_cap, uint new_usg, uint new_flow, uint32 time, bool new_shared, LinkProperties &cargo); static void DrawVertex(int x, int y, int size, int colour, int border_colour); }; diff --git a/src/main_gui.cpp b/src/main_gui.cpp index a102ff684b..33d3d5d311 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -435,6 +435,12 @@ struct MainWindow : Window } } + bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override + { + if (widget != WID_M_VIEWPORT) return false; + return this->viewport->overlay->ShowTooltip(pt, close_cond); + } + /** * Some data on this window has become invalid. * @param data Information about the changed data. diff --git a/src/window.cpp b/src/window.cpp index 0c3aaa5293..6d94dacfb3 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2873,6 +2873,7 @@ static void MouseLoop(MouseClick click, int mousewheel) _scrolling_viewport = true; _cursor.fix_at = (_settings_client.gui.scroll_mode == VSM_VIEWPORT_RMB_FIXED || _settings_client.gui.scroll_mode == VSM_MAP_RMB_FIXED); + DispatchRightClickEvent(w, x - w->left, y - w->top); return; } break;