#include "area_popup.h" #include #include #include "ap_state.h" #include "game_data.h" #include "global.h" #include "icons.h" #include "tracker_config.h" #include "tracker_panel.h" #include "tracker_state.h" AreaPopup::AreaPopup(wxWindow* parent, int area_id) : wxScrolledCanvas(parent, wxID_ANY), area_id_(area_id) { SetBackgroundStyle(wxBG_STYLE_PAINT); LoadIcons(); // TODO: This is slow on high-DPI screens. SetScrollRate(5, 5); SetBackgroundColour(*wxBLACK); Hide(); Bind(wxEVT_PAINT, &AreaPopup::OnPaint, this); Bind(wxEVT_DPI_CHANGED, &AreaPopup::OnDPIChanged, this); ResetIndicators(); } void AreaPopup::ResetIndicators() { indicators_.clear(); const MapArea& map_area = GD_GetMapArea(area_id_); wxFont the_font = GetFont().Scale(GetDPIScaleFactor()); TrackerPanel* tracker_panel = dynamic_cast(GetParent()); // Start calculating extents. wxMemoryDC mem_dc; mem_dc.SetFont(the_font.Bold()); header_extent_ = mem_dc.GetTextExtent(map_area.name); int acc_height = header_extent_.GetHeight() + FromDIP(20); int col_width = 0; mem_dc.SetFont(the_font); for (int section_id = 0; section_id < map_area.locations.size(); section_id++) { const Location& location = map_area.locations.at(section_id); if (IsLocationPostgame(location.ap_location_id)) { continue; } if (tracker_panel->IsPanelsMode()) { if (!location.single_panel) { continue; } } else { if (!AP_IsLocationVisible(location.classification) && !(location.hunt && GetTrackerConfig().show_hunt_panels)) { continue; } } indicators_.emplace_back(section_id, kLOCATION, acc_height); wxSize item_extent = mem_dc.GetTextExtent(location.name); int item_height = std::max(FromDIP(32), item_extent.GetHeight()) + FromDIP(10); acc_height += item_height; if (item_extent.GetWidth() > col_width) { col_width = item_extent.GetWidth(); } } if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { for (int painting_id : map_area.paintings) { if (IsPaintingPostgame(painting_id)) { continue; } indicators_.emplace_back(painting_id, kPAINTING, acc_height); const PaintingExit& painting = GD_GetPaintingExit(painting_id); wxSize item_extent = mem_dc.GetTextExtent(painting.display_name); int item_height = std::max(FromDIP(32), item_extent.GetHeight()) + FromDIP(10); acc_height += item_height; if (item_extent.GetWidth() > col_width) { col_width = item_extent.GetWidth(); } } } int item_width = col_width + FromDIP(10 + 32); full_width_ = std::max(header_extent_.GetWidth(), item_width) + FromDIP(20); full_height_ = acc_height; Fit(); SetVirtualSize(full_width_, full_height_); UpdateIndicators(); } void AreaPopup::UpdateIndicators() { const MapArea& map_area = GD_GetMapArea(area_id_); wxFont the_font = GetFont().Scale(GetDPIScaleFactor()); TrackerPanel* tracker_panel = dynamic_cast(GetParent()); rendered_ = wxBitmap(full_width_, full_height_); wxMemoryDC mem_dc; mem_dc.SelectObject(rendered_); mem_dc.SetPen(*wxTRANSPARENT_PEN); mem_dc.SetBrush(*wxBLACK_BRUSH); mem_dc.DrawRectangle({0, 0}, {full_width_, full_height_}); mem_dc.SetFont(the_font.Bold()); mem_dc.SetTextForeground(*wxWHITE); mem_dc.DrawText(map_area.name, {(full_width_ - header_extent_.GetWidth()) / 2, FromDIP(10)}); mem_dc.SetFont(the_font); for (const IndicatorInfo& indicator : indicators_) { switch (indicator.type) { case kLOCATION: { const Location& location = map_area.locations.at(indicator.id); bool checked = false; if (IsLocationWinCondition(location)) { checked = AP_HasReachedGoal(); } else if (tracker_panel->IsPanelsMode()) { const Panel& panel = GD_GetPanel(*location.single_panel); if (panel.non_counting) { checked = AP_HasCheckedGameLocation(location.ap_location_id); } else { checked = tracker_panel->GetSolvedPanels().contains(panel.nodepath); } } else { checked = AP_HasCheckedGameLocation(location.ap_location_id) || (location.hunt && AP_HasCheckedHuntPanel(location.ap_location_id)); } const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_; mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), indicator.y}); bool reachable = IsLocationReachable(location.ap_location_id); const wxColour* text_color = reachable ? wxWHITE : wxRED; mem_dc.SetTextForeground(*text_color); wxSize item_extent = mem_dc.GetTextExtent(location.name); mem_dc.DrawText( location.name, {FromDIP(10 + 32 + 10), indicator.y + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2}); break; } case kPAINTING: { const PaintingExit& painting = GD_GetPaintingExit(indicator.id); bool reachable = IsPaintingReachable(indicator.id); const wxColour* text_color = reachable ? wxWHITE : wxRED; mem_dc.SetTextForeground(*text_color); bool checked = reachable && AP_IsPaintingChecked(painting.internal_id); const wxBitmap* eye_ptr = checked ? checked_owl_ : unchecked_owl_; mem_dc.DrawBitmap(*eye_ptr, {FromDIP(10), indicator.y}); wxSize item_extent = mem_dc.GetTextExtent(painting.display_name); mem_dc.DrawText( painting.display_name, {FromDIP(10 + 32 + 10), indicator.y + (FromDIP(32) - mem_dc.GetFontMetrics().height) / 2}); break; } } } } void AreaPopup::OnPaint(wxPaintEvent& event) { wxBufferedPaintDC dc(this); PrepareDC(dc); dc.DrawBitmap(rendered_, 0, 0); event.Skip(); } void AreaPopup::OnDPIChanged(wxDPIChangedEvent& event) { LoadIcons(); ResetIndicators(); event.Skip(); } void AreaPopup::LoadIcons() { unchecked_eye_ = GetTheIconCache().GetIcon("assets/unchecked.png", FromDIP(wxSize{32, 32})); checked_eye_ = GetTheIconCache().GetIcon("assets/checked.png", FromDIP(wxSize{32, 32})); unchecked_owl_ = GetTheIconCache().GetIcon("assets/owl.png", FromDIP(wxSize{32, 32})); checked_owl_ = GetTheIconCache().GetIcon("assets/checked_owl.png", FromDIP(wxSize{32, 32})); }