Skip to content

Commit b8af60e

Browse files
authored
(New Feature) Added colorblind mode to all overlays (#634)
1 parent 582057b commit b8af60e

File tree

5 files changed

+63
-30
lines changed

5 files changed

+63
-30
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ The config folder in `C:/Users/<WINDOWS_USER>/.d4lf` contains:
107107
| minimum_overlay_font_size | The minimum font size for the vision overlay, specifically the green text that shows which filter(s) are matching. Note: For small profile names, the font may actually be larger than this size but will never go below this size. |
108108
| move_to_inv_item_type<br/>move_to_stash_item_type | Which types of items to move when using fast move functionality. Will only affect tabs defined in check_chest_tabs. You can select more than one option. <br>- `favorites`: Move favorites only <br>- `junk`: Move junk only <br>- `unmarked`: Only items not marked as favorite or junk <br>- `everything`: Move everything |
109109
| run_vision_mode_on_startup | If the vision mode should automatically start when starting d4lf. Otherwise has to be started manually with the vision button or the hotkey |
110+
| colorblind_mode | Enable a colorblind friendly palette for loot filter and paragon overlays |
110111
| vision_mode_type | Which vision mode you would like to use?. `highlight_matches` does the classic green highlighting of affixes on screen, but is slightly slower. `fast` just puts green text on screen but is very fast and works with controllers. |
111112

112113
| [char] | Description |

src/config/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ class GeneralModel(_IniBaseModel):
373373
)
374374
run_vision_mode_on_startup: bool = Field(default=True, description="Whether to run vision mode on startup or not")
375375
theme: ThemeType = Field(default=ThemeType.dark, description="Choose between light and dark theme for the GUI")
376+
colorblind_mode: bool = Field(
377+
default=False, description="Enable a colorblind friendly palette for loot filter and paragon overlays"
378+
)
376379
vision_mode_type: VisionModeType = Field(
377380
default=VisionModeType.highlight_matches,
378381
description="Should the vision mode use the slightly slower version that highlights matching affixes, or the immediate version that just shows text of the matches? Note: highlight_matches does not work with controllers.",

src/scripts/common.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import sys
55
import time
6+
from dataclasses import dataclass
67
from typing import TYPE_CHECKING
78

89
if TYPE_CHECKING:
@@ -30,11 +31,47 @@
3031

3132
SETUP_INSTRUCTIONS_URL = "https://github.com/d4lfteam/d4lf/blob/main/README.md#how-to-setup"
3233

33-
COLOR_GREEN = "#23fc5d" # Matched a profile
34-
COLOR_RED = "#fc2323" # Matched no profiles at all
35-
COLOR_ORANGE = "#fca503" # Matched a codex upgrade
36-
COLOR_GREY = "#888888" # Still processing or can't find the info we expect
37-
COLOR_BLUE = "#00b3b3" # We recognize this as an item, but it is not one we handle
34+
35+
@dataclass(frozen=True, slots=True)
36+
class FilterColors:
37+
"""Color palette used by the loot filter / vision overlays."""
38+
39+
matched: str
40+
no_match: str
41+
codex_upgrade: str
42+
processing: str
43+
unhandled: str
44+
45+
46+
# Default palette.
47+
FILTER_COLORS_DEFAULT = FilterColors(
48+
matched="#23fc5d", # COLOR_GREEN-Matched a profile
49+
no_match="#fc2323", # COLOR_RED-Matched no profiles at all
50+
codex_upgrade="#fca503", # COLOR_ORANGE-Matched a codex upgrade
51+
processing="#888888", # COLOR_GREY-Still processing or can't find the info we expect
52+
unhandled="#00b3b3", # COLOR_BLUE-We recognize this as an item, but it is not one we handle
53+
)
54+
55+
# Colorblind-friendly palette (Okabe-Ito inspired).
56+
FILTER_COLORS_COLORBLIND = FilterColors(
57+
matched="#56B4E9", # COLOR_BLUE-Matched a profile
58+
no_match="#D55E00", # COLOR_VERMILLION-Matched no profiles at all
59+
codex_upgrade="#E69F00", # COLOR_ORANGE-Matched a codex upgrade
60+
processing="#888888", # COLOR_GREY-Still processing or can't find the info we expect
61+
unhandled="#CC79A7", # COLOR_PURPLE-We recognize this as an item, but it is not one we handle
62+
)
63+
64+
65+
def get_filter_colors() -> FilterColors:
66+
"""Return the active palette (default vs. colorblind mode)."""
67+
try:
68+
if IniConfigLoader().general.colorblind_mode:
69+
return FILTER_COLORS_COLORBLIND
70+
except Exception:
71+
# Fail-safe: if config isn't available yet, use defaults.
72+
LOGGER.debug("get_filter_colors(): config unavailable; using default palette", exc_info=True)
73+
return FILTER_COLORS_DEFAULT
74+
3875

3976
ASPECT_UPGRADES_LABEL = "AspectUpgrades"
4077

src/scripts/vision_mode_fast.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from src.config.ui import ResManager
1313
from src.item.data.rarity import ItemRarity
1414
from src.item.filter import Filter, MatchedFilter
15-
from src.scripts.common import ASPECT_UPGRADES_LABEL, COLOR_GREEN, COLOR_ORANGE, is_ignored_item
15+
from src.scripts.common import ASPECT_UPGRADES_LABEL, get_filter_colors, is_ignored_item
1616
from src.tts import Publisher
1717
from src.utils.custom_mouse import mouse
1818
from src.utils.window import screenshot
@@ -135,15 +135,15 @@ def on_tts(self, _):
135135
match = res.keep
136136

137137
if match:
138-
color = COLOR_GREEN
138+
color = get_filter_colors().matched
139139
if not res.matched:
140140
if item_descr.rarity == ItemRarity.Unique:
141141
text = ["Unique"]
142142
elif item_descr.rarity == ItemRarity.Mythic:
143143
text = ["Mythic (Always Kept)"]
144144
else:
145145
if any(res_matched.profile.endswith(ASPECT_UPGRADES_LABEL) for res_matched in res.matched):
146-
color = COLOR_ORANGE
146+
color = get_filter_colors().codex_upgrade
147147
text = create_match_text(reversed(res.matched))
148148
return self.request_draw("\n".join(text), color)
149149
self.request_clear()

src/scripts/vision_mode_with_highlighting.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,7 @@
1919
from src.item.data.item_type import is_sigil
2020
from src.item.filter import Filter, FilterResult
2121
from src.item.find_descr import find_descr
22-
from src.scripts.common import (
23-
ASPECT_UPGRADES_LABEL,
24-
COLOR_BLUE,
25-
COLOR_GREEN,
26-
COLOR_GREY,
27-
COLOR_ORANGE,
28-
COLOR_RED,
29-
is_ignored_item,
30-
is_junk_rarity,
31-
reset_canvas,
32-
)
22+
from src.scripts.common import ASPECT_UPGRADES_LABEL, get_filter_colors, is_ignored_item, is_junk_rarity, reset_canvas
3323
from src.tts import Publisher
3424
from src.ui.char_inventory import CharInventory
3525
from src.ui.stash import Stash
@@ -221,44 +211,46 @@ def draw_empty_outline(self, item_roi, color, text: str | None):
221211

222212
def draw_match_outline(self, item_roi, should_keep_res, item_descr):
223213
x, y, w, h, off = self.get_coords_from_roi(item_roi)
224-
self.create_signal_rect(self.canvas, w, self.thick, COLOR_GREEN)
214+
self.create_signal_rect(self.canvas, w, self.thick, get_filter_colors().matched)
225215

226216
# show all info strings of the profiles
227217
text_y = h
228218
for match in reversed(should_keep_res.matched):
229-
text_y = self.draw_text(self.canvas, match.profile, COLOR_GREEN, text_y, 5, w // 2)
219+
text_y = self.draw_text(self.canvas, match.profile, get_filter_colors().matched, text_y, 5, w // 2)
230220
# Show matched bullets
231221
if item_descr and len(should_keep_res.matched) > 0:
232222
bullet_width = self.thick * 3
233223
for affix in should_keep_res.matched[0].matched_affixes:
234224
if affix.loc:
235-
self.draw_rect(self.canvas, bullet_width, affix, off, COLOR_GREEN)
225+
self.draw_rect(self.canvas, bullet_width, affix, off, get_filter_colors().matched)
236226

237227
if item_descr.aspect and item_descr.aspect.loc and any(m.did_match_aspect for m in should_keep_res.matched):
238-
self.draw_rect(self.canvas, bullet_width, item_descr.aspect, off, COLOR_GREEN)
228+
self.draw_rect(self.canvas, bullet_width, item_descr.aspect, off, get_filter_colors().matched)
239229

240230
self.root.update_idletasks()
241231
self.root.update()
242232

243233
def draw_no_match_outline(self, item_roi):
244234
x, y, w, h, off = self.get_coords_from_roi(item_roi)
245-
self.create_signal_rect(self.canvas, w, self.thick, COLOR_RED)
235+
self.create_signal_rect(self.canvas, w, self.thick, get_filter_colors().no_match)
246236
self.root.update_idletasks()
247237
self.root.update()
248238

249239
def draw_codex_upgrade_outline(self, item_roi, should_keep_result: FilterResult):
250240
x, y, w, h, off = self.get_coords_from_roi(item_roi)
251241

252-
self.create_signal_rect(self.canvas, w, self.thick, COLOR_ORANGE)
242+
self.create_signal_rect(self.canvas, w, self.thick, get_filter_colors().codex_upgrade)
253243

254244
# show string indicating that this item upgrades the codex
255245
if len(should_keep_result.matched) == 1 and should_keep_result.matched[0].profile == ASPECT_UPGRADES_LABEL:
256-
self.draw_text(self.canvas, "Codex Upgrade", COLOR_ORANGE, h, 5, w // 2)
246+
self.draw_text(self.canvas, "Codex Upgrade", get_filter_colors().codex_upgrade, h, 5, w // 2)
257247
else:
258248
# This matched an Aspects section in a profile, write the profiles
259249
text_y = h
260250
for match in reversed(should_keep_result.matched):
261-
text_y = self.draw_text(self.canvas, match.profile, COLOR_ORANGE, text_y, 5, w // 2)
251+
text_y = self.draw_text(
252+
self.canvas, match.profile, get_filter_colors().codex_upgrade, text_y, 5, w // 2
253+
)
262254

263255
self.root.update_idletasks()
264256
self.root.update()
@@ -346,12 +338,12 @@ def evaluate_item_and_queue_draw(self, item_descr: Item):
346338
if ignored_item:
347339
if item_descr.sanctified:
348340
self.request_empty_outline(
349-
item_descr, item_roi, COLOR_BLUE, "Sanctified (Not Supported)"
341+
item_descr, item_roi, get_filter_colors().unhandled, "Sanctified (Not Supported)"
350342
)
351343
else:
352-
self.request_empty_outline(item_descr, item_roi, COLOR_BLUE)
344+
self.request_empty_outline(item_descr, item_roi, get_filter_colors().unhandled)
353345
else:
354-
self.request_empty_outline(item_descr, item_roi, COLOR_GREY)
346+
self.request_empty_outline(item_descr, item_roi, get_filter_colors().processing)
355347

356348
# Since we've now drawn something we kick off a thread to remove the drawing
357349
# if the item is unselected. It is also automatically removed if a different

0 commit comments

Comments
 (0)