about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-03-10 12:45:14 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2025-03-10 12:45:14 -0400
commit79424e86dd7aa28c1b25868d86fa8ebffc801593 (patch)
treeebbc23fb998f14d69e638ce8b973b5a7da0b46c6
parentd3457700075fab2dac25bcff2775b7ae5a436a28 (diff)
downloadlingo-ap-tracker-79424e86dd7aa28c1b25868d86fa8ebffc801593.tar.gz
lingo-ap-tracker-79424e86dd7aa28c1b25868d86fa8ebffc801593.tar.bz2
lingo-ap-tracker-79424e86dd7aa28c1b25868d86fa8ebffc801593.zip
Optimized AreaPopup indicators
The list of indicators and the size of the window are calculated only when necessary (new connection, DPI changed, or hunt panel settings changed) and otherwise all we do is redraw the image.
-rw-r--r--src/area_popup.cpp147
-rw-r--r--src/area_popup.h25
-rw-r--r--src/tracker_frame.cpp16
-rw-r--r--src/tracker_frame.h1
-rw-r--r--src/tracker_panel.cpp15
-rw-r--r--src/tracker_panel.h2
6 files changed, 125 insertions, 81 deletions
diff --git a/src/area_popup.cpp b/src/area_popup.cpp index d7f45b6..a8e6004 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp
@@ -27,28 +27,26 @@ AreaPopup::AreaPopup(wxWindow* parent, int area_id)
27 Bind(wxEVT_PAINT, &AreaPopup::OnPaint, this); 27 Bind(wxEVT_PAINT, &AreaPopup::OnPaint, this);
28 Bind(wxEVT_DPI_CHANGED, &AreaPopup::OnDPIChanged, this); 28 Bind(wxEVT_DPI_CHANGED, &AreaPopup::OnDPIChanged, this);
29 29
30 UpdateIndicators(); 30 ResetIndicators();
31} 31}
32 32
33void AreaPopup::UpdateIndicators() { 33void AreaPopup::ResetIndicators() {
34 const MapArea& map_area = GD_GetMapArea(area_id_); 34 indicators_.clear();
35 35
36 const MapArea& map_area = GD_GetMapArea(area_id_);
36 wxFont the_font = GetFont().Scale(GetDPIScaleFactor()); 37 wxFont the_font = GetFont().Scale(GetDPIScaleFactor());
38 TrackerPanel* tracker_panel = dynamic_cast<TrackerPanel*>(GetParent());
37 39
38 // Start calculating extents. 40 // Start calculating extents.
39 wxMemoryDC mem_dc; 41 wxMemoryDC mem_dc;
40 mem_dc.SetFont(the_font.Bold()); 42 mem_dc.SetFont(the_font.Bold());
41 wxSize header_extent = mem_dc.GetTextExtent(map_area.name); 43 header_extent_ = mem_dc.GetTextExtent(map_area.name);
42 44
43 int acc_height = header_extent.GetHeight() + FromDIP(20); 45 int acc_height = header_extent_.GetHeight() + FromDIP(20);
44 int col_width = 0; 46 int col_width = 0;
45 47
46 mem_dc.SetFont(the_font); 48 mem_dc.SetFont(the_font);
47 49
48 TrackerPanel* tracker_panel = dynamic_cast<TrackerPanel*>(GetParent());
49
50 std::vector<int> real_locations;
51
52 for (int section_id = 0; section_id < map_area.locations.size(); 50 for (int section_id = 0; section_id < map_area.locations.size();
53 section_id++) { 51 section_id++) {
54 const Location& location = map_area.locations.at(section_id); 52 const Location& location = map_area.locations.at(section_id);
@@ -67,7 +65,7 @@ void AreaPopup::UpdateIndicators() {
67 } 65 }
68 } 66 }
69 67
70 real_locations.push_back(section_id); 68 indicators_.emplace_back(section_id, kLOCATION, acc_height);
71 69
72 wxSize item_extent = mem_dc.GetTextExtent(location.name); 70 wxSize item_extent = mem_dc.GetTextExtent(location.name);
73 int item_height = 71 int item_height =
@@ -85,6 +83,8 @@ void AreaPopup::UpdateIndicators() {
85 continue; 83 continue;
86 } 84 }
87 85
86 indicators_.emplace_back(painting_id, kPAINTING, acc_height);
87
88 const PaintingExit& painting = GD_GetPaintingExit(painting_id); 88 const PaintingExit& painting = GD_GetPaintingExit(painting_id);
89 wxSize item_extent = mem_dc.GetTextExtent(painting.display_name); 89 wxSize item_extent = mem_dc.GetTextExtent(painting.display_name);
90 int item_height = 90 int item_height =
@@ -98,84 +98,91 @@ void AreaPopup::UpdateIndicators() {
98 } 98 }
99 99
100 int item_width = col_width + FromDIP(10 + 32); 100 int item_width = col_width + FromDIP(10 + 32);
101 int full_width = std::max(header_extent.GetWidth(), item_width) + FromDIP(20); 101 full_width_ = std::max(header_extent_.GetWidth(), item_width) + FromDIP(20);
102 full_height_ = acc_height;
102 103
103 Fit(); 104 Fit();
104 SetVirtualSize(full_width, acc_height); 105 SetVirtualSize(full_width_, full_height_);
106
107 UpdateIndicators();
108}
109
110void AreaPopup::UpdateIndicators() {
111 const MapArea& map_area = GD_GetMapArea(area_id_);
112 wxFont the_font = GetFont().Scale(GetDPIScaleFactor());
113 TrackerPanel* tracker_panel = dynamic_cast<TrackerPanel*>(GetParent());
114
115 rendered_ = wxBitmap(full_width_, full_height_);
105 116
106 rendered_ = wxBitmap(full_width, acc_height); 117 wxMemoryDC mem_dc;
107 mem_dc.SelectObject(rendered_); 118 mem_dc.SelectObject(rendered_);
108 mem_dc.SetPen(*wxTRANSPARENT_PEN); 119 mem_dc.SetPen(*wxTRANSPARENT_PEN);
109 mem_dc.SetBrush(*wxBLACK_BRUSH); 120 mem_dc.SetBrush(*wxBLACK_BRUSH);
110 mem_dc.DrawRectangle({0, 0}, {full_width, acc_height}); 121 mem_dc.DrawRectangle({0, 0}, {full_width_, full_height_});
111 122
112 mem_dc.SetFont(the_font.Bold()); 123 mem_dc.SetFont(the_font.Bold());
113 mem_dc.SetTextForeground(*wxWHITE); 124 mem_dc.SetTextForeground(*wxWHITE);
114 mem_dc.DrawText(map_area.name, 125 mem_dc.DrawText(map_area.name,
115 {(full_width - header_extent.GetWidth()) / 2, FromDIP(10)}); 126 {(full_width_ - header_extent_.GetWidth()) / 2, FromDIP(10)});
116
117 int cur_height = header_extent.GetHeight() + FromDIP(20);
118 127
119 mem_dc.SetFont(the_font); 128 mem_dc.SetFont(the_font);
120 129
121 for (int section_id : real_locations) { 130 for (const IndicatorInfo& indicator : indicators_) {
122 const Location& location = map_area.locations.at(section_id); 131 switch (indicator.type) {
123 132 case kLOCATION: {
124 bool checked = false; 133 const Location& location = map_area.locations.at(indicator.id);
125 if (IsLocationWinCondition(location)) { 134
126 checked = AP_HasReachedGoal(); 135 bool checked = false;
127 } else if (tracker_panel->IsPanelsMode()) { 136 if (IsLocationWinCondition(location)) {
128 const Panel& panel = GD_GetPanel(*location.single_panel); 137 checked = AP_HasReachedGoal();
129 if (panel.non_counting) { 138 } else if (tracker_panel->IsPanelsMode()) {
130 checked = AP_HasCheckedGameLocation(location.ap_location_id); 139 const Panel& panel = GD_GetPanel(*location.single_panel);
131 } else { 140 if (panel.non_counting) {
132 checked = tracker_panel->GetSolvedPanels().contains(panel.nodepath); 141 checked = AP_HasCheckedGameLocation(location.ap_location_id);
142 } else {
143 checked = tracker_panel->GetSolvedPanels().contains(panel.nodepath);
144 }
145 } else {
146 checked = AP_HasCheckedGameLocation(location.ap_location_id) ||
147 (location.hunt &&
148 AP_HasCheckedHuntPanel(location.ap_location_id));
149 }
150
151 const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_;
152
153 mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), indicator.y});
154
155 bool reachable = IsLocationReachable(location.ap_location_id);
156 const wxColour* text_color = reachable ? wxWHITE : wxRED;
157 mem_dc.SetTextForeground(*text_color);
158
159 wxSize item_extent = mem_dc.GetTextExtent(location.name);
160 mem_dc.DrawText(
161 location.name,
162 {FromDIP(10 + 32 + 10),
163 indicator.y + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2});
164
165 break;
133 } 166 }
134 } else { 167 case kPAINTING: {
135 checked = 168 const PaintingExit& painting = GD_GetPaintingExit(indicator.id);
136 AP_HasCheckedGameLocation(location.ap_location_id) ||
137 (location.hunt && AP_HasCheckedHuntPanel(location.ap_location_id));
138 }
139
140 const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_;
141 169
142 mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), cur_height}); 170 bool reachable = IsPaintingReachable(indicator.id);
171 const wxColour* text_color = reachable ? wxWHITE : wxRED;
172 mem_dc.SetTextForeground(*text_color);
143 173
144 bool reachable = IsLocationReachable(location.ap_location_id); 174 bool checked = reachable && AP_IsPaintingChecked(painting.internal_id);
145 const wxColour* text_color = reachable ? wxWHITE : wxRED; 175 const wxBitmap* eye_ptr = checked ? checked_owl_ : unchecked_owl_;
146 mem_dc.SetTextForeground(*text_color); 176 mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), indicator.y});
147 177
148 wxSize item_extent = mem_dc.GetTextExtent(location.name); 178 wxSize item_extent = mem_dc.GetTextExtent(painting.display_name);
149 mem_dc.DrawText( 179 mem_dc.DrawText(
150 location.name, 180 painting.display_name,
151 {FromDIP(10 + 32 + 10), 181 {FromDIP(10 + 32 + 10),
152 cur_height + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2}); 182 indicator.y + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2});
153
154 cur_height += FromDIP(10 + 32);
155 }
156 183
157 if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { 184 break;
158 for (int painting_id : map_area.paintings) {
159 if (IsPaintingPostgame(painting_id)) {
160 continue;
161 } 185 }
162
163 const PaintingExit& painting = GD_GetPaintingExit(painting_id);
164
165 bool reachable = IsPaintingReachable(painting_id);
166 const wxColour* text_color = reachable ? wxWHITE : wxRED;
167 mem_dc.SetTextForeground(*text_color);
168
169 bool checked = reachable && AP_IsPaintingChecked(painting.internal_id);
170 const wxBitmap* eye_ptr = checked ? checked_owl_ : unchecked_owl_;
171 mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), cur_height});
172
173 wxSize item_extent = mem_dc.GetTextExtent(painting.display_name);
174 mem_dc.DrawText(painting.display_name,
175 {FromDIP(10 + 32 + 10),
176 cur_height + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2});
177
178 cur_height += FromDIP(10 + 32);
179 } 186 }
180 } 187 }
181} 188}
@@ -190,7 +197,7 @@ void AreaPopup::OnPaint(wxPaintEvent& event) {
190 197
191void AreaPopup::OnDPIChanged(wxDPIChangedEvent& event) { 198void AreaPopup::OnDPIChanged(wxDPIChangedEvent& event) {
192 LoadIcons(); 199 LoadIcons();
193 UpdateIndicators(); 200 ResetIndicators();
194 201
195 event.Skip(); 202 event.Skip();
196} 203}
diff --git a/src/area_popup.h b/src/area_popup.h index 2401e4e..50e10e8 100644 --- a/src/area_popup.h +++ b/src/area_popup.h
@@ -7,13 +7,32 @@
7#include <wx/wx.h> 7#include <wx/wx.h>
8#endif 8#endif
9 9
10#include <vector>
11
10class AreaPopup : public wxScrolledCanvas { 12class AreaPopup : public wxScrolledCanvas {
11 public: 13 public:
12 AreaPopup(wxWindow* parent, int area_id); 14 AreaPopup(wxWindow* parent, int area_id);
13 15
16 void ResetIndicators();
14 void UpdateIndicators(); 17 void UpdateIndicators();
15 18
16 private: 19 private:
20 enum IndicatorType {
21 kLOCATION,
22 kPAINTING,
23 };
24
25 struct IndicatorInfo {
26 // For locations, the id is an index into the map area's locations list.
27 // For paintings, it is a real painting id.
28 int id;
29 IndicatorType type;
30 int y;
31
32 IndicatorInfo(int id, IndicatorType type, int y)
33 : id(id), type(type), y(y) {}
34 };
35
17 void OnPaint(wxPaintEvent& event); 36 void OnPaint(wxPaintEvent& event);
18 void OnDPIChanged(wxDPIChangedEvent& event); 37 void OnDPIChanged(wxDPIChangedEvent& event);
19 38
@@ -26,6 +45,12 @@ class AreaPopup : public wxScrolledCanvas {
26 const wxBitmap* unchecked_owl_; 45 const wxBitmap* unchecked_owl_;
27 const wxBitmap* checked_owl_; 46 const wxBitmap* checked_owl_;
28 47
48 int full_width_ = 0;
49 int full_height_ = 0;
50 wxSize header_extent_;
51
52 std::vector<IndicatorInfo> indicators_;
53
29 wxBitmap rendered_; 54 wxBitmap rendered_;
30}; 55};
31 56
diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index 6a4ab2e..fa68582 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp
@@ -255,8 +255,9 @@ void TrackerFrame::OnSettings(wxCommandEvent &event) {
255 GetTrackerConfig().track_position = dlg.GetTrackPosition(); 255 GetTrackerConfig().track_position = dlg.GetTrackPosition();
256 GetTrackerConfig().Save(); 256 GetTrackerConfig().Save();
257 257
258 UpdateIndicators( 258 UpdateIndicators(StateUpdate{.cleared_locations = true,
259 StateUpdate{.cleared_locations = true, .player_position = true}); 259 .player_position = true,
260 .changed_settings = true});
260 } 261 }
261} 262}
262 263
@@ -328,7 +329,7 @@ void TrackerFrame::OnSashPositionChanged(wxSplitterEvent& event) {
328} 329}
329 330
330void TrackerFrame::OnStateReset(wxCommandEvent &event) { 331void TrackerFrame::OnStateReset(wxCommandEvent &event) {
331 tracker_panel_->UpdateIndicators(); 332 tracker_panel_->UpdateIndicators(/*reset=*/true);
332 achievements_pane_->UpdateIndicators(); 333 achievements_pane_->UpdateIndicators();
333 items_pane_->ResetIndicators(); 334 items_pane_->ResetIndicators();
334 options_pane_->OnConnect(); 335 options_pane_->OnConnect();
@@ -350,7 +351,7 @@ void TrackerFrame::OnStateChanged(StateChangedEvent &event) {
350 panels_panel_->SetPanelsMode(); 351 panels_panel_->SetPanelsMode();
351 notebook_->AddPage(panels_panel_, "Panels"); 352 notebook_->AddPage(panels_panel_, "Panels");
352 } 353 }
353 panels_panel_->UpdateIndicators(); 354 panels_panel_->UpdateIndicators(/*reset=*/false);
354 if (notebook_->GetSelection() == 2) { 355 if (notebook_->GetSelection() == 2) {
355 Refresh(); 356 Refresh();
356 } 357 }
@@ -361,10 +362,13 @@ void TrackerFrame::OnStateChanged(StateChangedEvent &event) {
361 if (!state.items.empty() || !state.paintings.empty() || 362 if (!state.items.empty() || !state.paintings.empty() ||
362 state.cleared_locations || 363 state.cleared_locations ||
363 (state.hunt_panels && GetTrackerConfig().show_hunt_panels)) { 364 (state.hunt_panels && GetTrackerConfig().show_hunt_panels)) {
364 tracker_panel_->UpdateIndicators(); 365 // TODO: The only real reason to reset tracker_panel during an active
366 // connection is if the hunt panels setting changes. If we remove hunt
367 // panels later, we can get rid of this.
368 tracker_panel_->UpdateIndicators(/*reset=*/state.changed_settings);
365 subway_map_->UpdateIndicators(); 369 subway_map_->UpdateIndicators();
366 if (panels_panel_ != nullptr) { 370 if (panels_panel_ != nullptr) {
367 panels_panel_->UpdateIndicators(); 371 panels_panel_->UpdateIndicators(/*reset=*/false);
368 } 372 }
369 Refresh(); 373 Refresh();
370 } else if (state.player_position && GetTrackerConfig().track_position) { 374 } else if (state.player_position && GetTrackerConfig().track_position) {
diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 7704208..131c7b8 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h
@@ -57,6 +57,7 @@ struct StateUpdate {
57 bool cleared_locations = false; 57 bool cleared_locations = false;
58 bool hunt_panels = false; 58 bool hunt_panels = false;
59 bool player_position = false; 59 bool player_position = false;
60 bool changed_settings = false;
60}; 61};
61 62
62class StateChangedEvent : public wxEvent { 63class StateChangedEvent : public wxEvent {
diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index b4e6697..64e6ab3 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp
@@ -50,13 +50,19 @@ TrackerPanel::TrackerPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) {
50 Bind(wxEVT_MOTION, &TrackerPanel::OnMouseMove, this); 50 Bind(wxEVT_MOTION, &TrackerPanel::OnMouseMove, this);
51} 51}
52 52
53void TrackerPanel::UpdateIndicators() { 53void TrackerPanel::UpdateIndicators(bool reset) {
54 if (panels_mode_ && !savedata_path_) { 54 if (panels_mode_ && !savedata_path_) {
55 solved_panels_ = IPC_GetSolvedPanels(); 55 solved_panels_ = IPC_GetSolvedPanels();
56 } 56 }
57 57
58 for (AreaIndicator &area : areas_) { 58 if (reset) {
59 area.popup->UpdateIndicators(); 59 for (AreaIndicator &area : areas_) {
60 area.popup->ResetIndicators();
61 }
62 } else {
63 for (AreaIndicator &area : areas_) {
64 area.popup->UpdateIndicators();
65 }
60 } 66 }
61 67
62 Redraw(); 68 Redraw();
@@ -74,6 +80,7 @@ void TrackerPanel::SetSavedataPath(std::string savedata_path) {
74 savedata_path_ = savedata_path; 80 savedata_path_ = savedata_path;
75 panels_mode_ = true; 81 panels_mode_ = true;
76 82
83 UpdateIndicators(/*reset=*/true);
77 RefreshSavedata(); 84 RefreshSavedata();
78} 85}
79 86
@@ -90,7 +97,7 @@ void TrackerPanel::RefreshSavedata() {
90 } 97 }
91 } 98 }
92 99
93 UpdateIndicators(); 100 UpdateIndicators(/*reset=*/false);
94 Refresh(); 101 Refresh();
95} 102}
96 103
diff --git a/src/tracker_panel.h b/src/tracker_panel.h index b7067f5..ae89a35 100644 --- a/src/tracker_panel.h +++ b/src/tracker_panel.h
@@ -17,7 +17,7 @@ class TrackerPanel : public wxPanel {
17 public: 17 public:
18 TrackerPanel(wxWindow *parent); 18 TrackerPanel(wxWindow *parent);
19 19
20 void UpdateIndicators(); 20 void UpdateIndicators(bool reset);
21 21
22 void SetPanelsMode(); 22 void SetPanelsMode();
23 23