Skip to content

Commit 5fd75a2

Browse files
committed
Fix label backgrounds; sync state from device; refine ANC and tray
- Labels/checkboxes are transparent now (no dark boxes inside cards). - ANC strength row only shows when noise cancelling is active. - On connect, sync from the device: ANC queries current mode, EQ loads the current custom bands and highlights the active preset. - Tray quick controls are now top-level items, not in a submenu. https://claude.ai/code/session_014KrpftzUgVFGFXVUU3DUTT
1 parent 7bdf10d commit 5fd75a2

5 files changed

Lines changed: 55 additions & 7 deletions

File tree

src/nothing_ear/bluetooth/protocol.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,7 @@ def apply_custom_eq(self, bands) -> None:
260260
def get_custom_eq(self):
261261
resp = self.query_raw(CMD_CUSTOM_EQ_STATUS)
262262
return decode_custom_eq(frame_payload(resp))
263+
264+
def get_eq_mode(self) -> int | None:
265+
payload = frame_payload(self.query_raw(0xC01F))
266+
return payload[0] if payload else None

src/nothing_ear/ui/anc_tab.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def _build(self) -> None:
119119

120120
level_card = QFrame()
121121
level_card.setObjectName("card")
122+
self._level_card = level_card
122123
lv = QVBoxLayout(level_card)
123124
lv.setContentsMargins(18, 18, 18, 18)
124125
lv.setSpacing(12)
@@ -144,6 +145,7 @@ def _build(self) -> None:
144145
root.addStretch(1)
145146

146147
self._set_enabled(False)
148+
self._render()
147149

148150
def _chip(self, label: str, on_click) -> QPushButton:
149151
btn = QPushButton(label)
@@ -209,3 +211,4 @@ def _render(self) -> None:
209211
self._anc_btn.setChecked(anc_on)
210212
for mode, btn in self._level_buttons.items():
211213
btn.setChecked(self._active == mode)
214+
self._level_card.setVisible(anc_on)

src/nothing_ear/ui/eq_tab.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,17 @@
5454
]
5555

5656

57+
_PRESET_BY_VALUE = {
58+
0: EQPreset.BALANCED,
59+
1: EQPreset.VOICE,
60+
2: EQPreset.MORE_TREBLE,
61+
3: EQPreset.MORE_BASS,
62+
}
63+
64+
5765
class _EqWorker(QObject):
5866
applied = Signal()
67+
loaded = Signal(object, object)
5968
failed = Signal(str)
6069

6170
def __init__(self) -> None:
@@ -69,6 +78,9 @@ def set_preset(self, preset: EQPreset) -> None:
6978
def apply_custom(self, bands: list[EqBand]) -> None:
7079
self._spawn(self._apply_custom, bands)
7180

81+
def load(self) -> None:
82+
self._spawn(self._load)
83+
7284
def _spawn(self, fn, *args) -> None:
7385
if self.address:
7486
threading.Thread(target=fn, args=args, daemon=True).start()
@@ -89,6 +101,15 @@ def _apply_custom(self, bands: list[EqBand]) -> None:
89101
except Exception as exc:
90102
self.failed.emit(str(exc))
91103

104+
def _load(self) -> None:
105+
try:
106+
with NothingController(self.address, channel=self.channel) as ctrl:
107+
bands = ctrl.get_custom_eq()
108+
mode = ctrl.get_eq_mode()
109+
self.loaded.emit(bands, mode)
110+
except Exception as exc:
111+
self.failed.emit(str(exc))
112+
92113

93114
class EqTab(QWidget):
94115
def __init__(self, mock: bool = False) -> None:
@@ -98,6 +119,7 @@ def __init__(self, mock: bool = False) -> None:
98119

99120
self._worker = _EqWorker()
100121
self._worker.applied.connect(lambda: self._set_hint("Applied."))
122+
self._worker.loaded.connect(self._on_loaded)
101123
self._worker.failed.connect(lambda m: self._set_hint(f"Error: {m}"))
102124

103125
self._build()
@@ -179,15 +201,29 @@ def _build(self) -> None:
179201
self._set_enabled(False)
180202

181203
def set_device(self, address: str | None, channel: int = 15) -> None:
204+
changed = address != self._address
182205
self._address = address
183206
self._worker.address = address
184207
self._worker.channel = channel
185208
self._set_enabled(address is not None)
186-
self._set_hint(
187-
"Drag the points, or import a profile, then Apply."
188-
if address
189-
else "No connected Nothing device."
190-
)
209+
if address is None:
210+
self._set_hint("No connected Nothing device.")
211+
elif changed and not self._mock:
212+
self._set_hint("Reading current EQ from device ...")
213+
self._worker.load()
214+
elif self._mock:
215+
self._set_hint("Mock mode: commands are not sent.")
216+
217+
def _on_loaded(self, bands, mode) -> None:
218+
if isinstance(bands, list) and len(bands) >= len(self._curve.bands()):
219+
self._curve.set_bands(bands[: len(self._curve.bands())])
220+
active = _PRESET_BY_VALUE.get(mode)
221+
for btn, (_label, preset) in zip(self._preset_buttons, _PRESETS):
222+
btn.setChecked(preset == active)
223+
if active is not None:
224+
self._set_hint(f"Synced. Active preset: {active.value}.")
225+
else:
226+
self._set_hint("Synced custom EQ from device.")
191227

192228
def _set_enabled(self, enabled: bool) -> None:
193229
self._apply_btn.setEnabled(enabled)

src/nothing_ear/ui/main_window.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,11 @@ def _setup_tray(self) -> None:
103103
open_action = QAction("Open", self)
104104
open_action.triggered.connect(self._show_window)
105105
menu.addAction(open_action)
106+
menu.addSeparator()
106107

107-
anc_menu = menu.addMenu("Noise Cancelling")
108+
header = QAction("Noise Cancelling", self)
109+
header.setEnabled(False)
110+
menu.addAction(header)
108111
tray_modes = [
109112
("Off", ANCMode.OFF),
110113
("Transparency", ANCMode.TRANSPARENCY),
@@ -116,7 +119,7 @@ def _setup_tray(self) -> None:
116119
for label, mode in tray_modes:
117120
action = QAction(label, self)
118121
action.triggered.connect(lambda _checked=False, m=mode: self._anc_tab.apply(m))
119-
anc_menu.addAction(action)
122+
menu.addAction(action)
120123

121124
menu.addSeparator()
122125
quit_action = QAction("Quit", self)

src/nothing_ear/ui/style.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
font-size: 13px;
1313
}
1414
15+
QLabel, QCheckBox { background: transparent; }
16+
1517
#appTitle { font-size: 22px; font-weight: 700; letter-spacing: 0.3px; color: #fafafa; }
1618
#appSubtitle { color: #82828b; font-size: 12px; }
1719
#sectionTitle { font-size: 12px; font-weight: 600; color: #b9b9c0; letter-spacing: 0.6px; }

0 commit comments

Comments
 (0)