Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ The whole tool works by reading the clipboard, so this is worth being precise ab

| path | what |
|---|---|
| `<config>/config.json` | your settings: league, hotkeys, panel geometry, listing status, result count, filter ranges, client and interface language, panel opacity, whether to update automatically - **and your QuickPaste entries, in full**, since they are text you typed for this tool to hold |
| `<config>/config.json` | your settings: league, hotkeys, panel geometry, listing status, result count, filter ranges, client and interface language, panel opacity, whether the status indicator is shown, whether to update automatically - **and your QuickPaste entries, in full**, since they are text you typed for this tool to hold |
| `<config>/cookies.txt` | the cookie jar above |
| `<cache>/data/<version>/` | the downloaded game-data bundle, plus a `current` pointer |
| `<cache>/update/` | a downloaded release of the application, waiting for the restart that applies it. One file, consumed as it is applied; absent whenever no update is pending |
Expand Down
11 changes: 9 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,20 @@ while a click on our own card would; and `place_overlay` logs the geometry it ch
thing to read when the panel lands somewhere unexpected.

The **idle status marker** replaces the old "● PPC" spike: two lines — `PoPC v<version>` and the data
bundle's version — in outlined yellow at half opacity over the mana globe, which is where the game
bundle's version — in yellow inside a light-grey halo at half opacity over the mana globe, which is where the game
itself has nothing to say. `Config::status_right`/`status_bottom` place its centre, as offsets from
the game window's bottom-right corner ÷ its height (the same reasoning as the frame edges), and they
are config-file-only. Horizontally that centre is the globe's; vertically it sits below it, in the
globe's lower half, so that the third line — the one an available update adds — still lands on the
glass instead of on the frame. `place_overlay` sizes the window to the text for that screen, so the idle
overlay is a 200×48 rectangle rather than a dialog-sized one nothing is drawn into.
overlay is a 200×48 rectangle rather than a dialog-sized one nothing is drawn into. The outline
stamped around the glyphs is light grey, not black: the globe behind it is dark whenever the mana
is spent or fully reserved, and that is exactly where a black outline vanished. Its per-stamp
alpha is not the line's — eight stamps of one string composite rather than add, so a visible halo
pixel lands at `1-(1-s)^n` for the `n≈3` a straight edge gets, and that is what the constant is
solved back through.
`Config::status_marker` (Settings → General → Appearance, on by default) turns the marker off
altogether; with it off the idle overlay is never mapped, and nothing else about a check changes.

**Settings is four tabs** — General, Price check, QuickPaste, Application — between a fixed header (the title
and the close disc) and a fixed footer (Save). `kTabs` in `settings_screen.cpp` pairs each name with
Expand Down
39 changes: 28 additions & 11 deletions src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <algorithm>
#include <clocale>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <sstream>
Expand Down Expand Up @@ -163,17 +164,26 @@ std::string update_status_line(const update::Updater::Status& st) {
}
}

/// One centred line in yellow with a black outline, drawn straight into the draw list: the
/// One centred line in yellow inside a light-grey halo, drawn straight into the draw list: the
/// overlay has no background of its own here, so the text is over whatever the game is showing
/// and needs to carry its own contrast. The outline is the same string stamped at eight offsets
/// around the glyphs, which is cheap at two lines and needs no shader.
/// and needs to carry its own contrast. The halo is the same string stamped at eight offsets
/// around the glyphs, which is cheap at three lines and needs no shader. Grey and not black,
/// because the globe behind it is dark whenever the mana is spent or fully reserved and a black
/// outline vanished there; grey and not white, because the text still has to hold its shape
/// against a full globe.
void draw_outlined_line(const char* text, float centre_x, float y, float alpha) {
ImDrawList* dl = ImGui::GetWindowDrawList();
ImFont* font = ImGui::GetFont();
const float size = ImGui::GetFontSize();
const ImVec2 extent = ImGui::CalcTextSize(text);
const ImVec2 at(centre_x - extent.x * 0.5f, y);
const ImU32 outline = IM_COL32(0, 0, 0, static_cast<int>(alpha * 255));
// The stamps composite over one another rather than adding, so a pixel the halo covers n
// times lands at 1-(1-s)^n. Outside the glyphs — the only part of the halo ever seen — n is
// three along a straight edge, since only the offsets pointing into the glyph reach it, and
// that is what `alpha` is solved back through here. Dividing it by eight instead assumes
// additive blending and a coverage no visible halo pixel has.
const float stamp = 1.0f - std::pow(1.0f - alpha, 1.0f / 3.0f);
const ImU32 outline = IM_COL32(150, 150, 150, static_cast<int>(stamp * 255));
for (const ImVec2 d : {ImVec2(-1, -1), ImVec2(0, -1), ImVec2(1, -1), ImVec2(-1, 0),
ImVec2(1, 0), ImVec2(-1, 1), ImVec2(0, 1), ImVec2(1, 1)})
dl->AddText(font, size, ImVec2(at.x + d.x, at.y + d.y), outline, text);
Expand Down Expand Up @@ -201,10 +211,10 @@ void draw_status_marker(App& app) {
const float top = (io.DisplaySize.y - line_h * lines) * 0.5f;
draw_outlined_line(version.c_str(), io.DisplaySize.x * 0.5f, top, kStatusAlpha);
draw_outlined_line(data.c_str(), io.DisplaySize.x * 0.5f, top + line_h, kStatusAlpha);
// Fully opaque where the other two are half: this one is the only line here that is asking
// for something rather than reporting state.
// Drawn like the other two: what marks this line out is that it is only there at all when
// something is waiting.
if (!news.empty())
draw_outlined_line(news.c_str(), io.DisplaySize.x * 0.5f, top + line_h * 2, 1.0f);
draw_outlined_line(news.c_str(), io.DisplaySize.x * 0.5f, top + line_h * 2, kStatusAlpha);
ImGui::PopFont();
ImGui::End();
}
Expand Down Expand Up @@ -1087,7 +1097,11 @@ void App::update_overlay_placement() {
}

bool moved = g.x != game_x_ || g.y != game_y_ || g.w != game_w_ || g.h != game_h_;
if (game_present_ && overlay_.visible() && !moved) {
// With the marker off there is nothing to draw while idle, so the window stays unmapped
// instead of mapped over an empty rectangle. Geometry is still tracked below, so the first
// panel to open is placed against the game as it is now.
const bool want_visible = screen_ != Screen::Hidden || config_.status_marker;
if (game_present_ && overlay_.visible() == want_visible && !moved) {
// Back from another application without the window having moved: still owed the
// keyboard, since it was lost to whatever was in front and no window manager will hand
// it to a window it does not manage.
Expand All @@ -1101,9 +1115,9 @@ void App::update_overlay_placement() {
game_h_ = g.h;

place_overlay();
if (!overlay_.visible()) {
overlay_.set_visible(true);
overlay_set_click_through(overlay_.window(), screen_ == Screen::Hidden);
if (overlay_.visible() != want_visible) {
overlay_.set_visible(want_visible);
if (want_visible) overlay_set_click_through(overlay_.window(), screen_ == Screen::Hidden);
}
if (screen_ != Screen::Hidden) {
SDL_RaiseWindow(overlay_.window());
Expand Down Expand Up @@ -1369,6 +1383,9 @@ void App::set_screen(Screen s) {
if (!active) copy_pending_ = false; // nothing left to fill in; stop watching the clipboard
place_overlay(); // each screen has its own geometry; apply before showing
if (active && !overlay_.visible()) overlay_.set_visible(true);
// Closing with the marker turned off leaves nothing to show, so unmap rather than sit
// mapped and empty over the game.
if (!active && !config_.status_marker && overlay_.visible()) overlay_.set_visible(false);
// The window stays mapped; interactivity is what changes. Idle == click-through
// so input passes to the game; active == catch input and raise to the front.
overlay_set_click_through(overlay_.window(), !active);
Expand Down
2 changes: 2 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ void read_into(Config& c, const json& j) {
c.inventory_edge = j.value("inventory_edge", c.inventory_edge);
c.status_right = j.value("status_right", c.status_right);
c.status_bottom = j.value("status_bottom", c.status_bottom);
c.status_marker = j.value("status_marker", c.status_marker);
c.reduce_transparency = j.value("reduce_transparency", c.reduce_transparency);
c.auto_update = j.value("auto_update", c.auto_update);
c.debug_log = j.value("debug_log", c.debug_log);
Expand Down Expand Up @@ -125,6 +126,7 @@ bool Config::save() const {
j["inventory_edge"] = inventory_edge;
j["status_right"] = status_right;
j["status_bottom"] = status_bottom;
j["status_marker"] = status_marker;
j["reduce_transparency"] = reduce_transparency;
j["auto_update"] = auto_update;
j["debug_log"] = debug_log;
Expand Down
5 changes: 5 additions & 0 deletions src/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ struct Config {
float status_right = 0.110f; ///< marker centre's distance from the right edge, ÷ game height
float status_bottom = 0.070f; ///< marker centre's distance from the bottom edge, ÷ game height

/// Draw that marker at all. On by default — it is the only sign the overlay is running —
/// but it is text over the game's HUD, so it is a knob. Off leaves the idle overlay
/// unmapped entirely; nothing else about a price check changes.
bool status_marker = true;

/// Draw every panel's background solid instead of letting the game through it. An
/// accessibility setting: text over a moving background is the hard case this answers.
/// Opacity and not a blur — see ui/theme.hpp for why a blur is not available to us.
Expand Down
6 changes: 6 additions & 0 deletions src/screens/settings_screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ void general_tab(App& app, Config& c) {
ImGui::PushTextWrapPos(0.0f);
ImGui::TextDisabled("%s", ui::text(ui::Msg::ReduceTransparencyHelp));
ImGui::PopTextWrapPos();

ImGui::Checkbox(row(ui::text(ui::Msg::StatusMarker)), &c.status_marker);
row_gutter();
ImGui::PushTextWrapPos(0.0f);
ImGui::TextDisabled("%s", ui::text(ui::Msg::StatusMarkerHelp));
ImGui::PopTextWrapPos();
}

void price_check_tab(App& app, Config& c) {
Expand Down
3 changes: 3 additions & 0 deletions src/ui/strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ constexpr const char* kEnglish[]{

"Reduce transparency",
"Draws the panels solid instead of letting the game show through them.",
"Show the status indicator",
"The version and data lines over the mana globe. Turning them off hides nothing else \xe2\x80\x94 "
"price checks still open as usual.",

"Docks beside whichever game panel the cursor was over.",
"Width",
Expand Down
2 changes: 2 additions & 0 deletions src/ui/strings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ enum class Msg : uint16_t {

ReduceTransparency,
ReduceTransparencyHelp,
StatusMarker,
StatusMarkerHelp,

PanelHelp,
PanelWidth,
Expand Down