mirror of
https://github.com/CodeZeno/Claude-Code-Usage-Monitor.git
synced 2026-06-20 10:19:27 +08:00
refactor: read display flags from state for merge compatibility
Revert shared window.rs/poller.rs signatures to base. Detailed-remaining is read from a lock-free poller atomic (countdown formatting) and a shared window-level text-width slot (layout). The slot mechanism block is identical to the ETD branch's so any merge subset stays conflict-free; each branch wires only its own slot. Relocate the localization field to a distinct anchor (after exit).
This commit is contained in:
parent
fd94e91612
commit
39a9166109
@ -59,8 +59,8 @@ Once running, it will appear in your taskbar and as one or more tray icons in th
|
||||
- Drag the left divider to move the taskbar widget
|
||||
- Right-click the taskbar widget or tray icon for refresh, displayed models, update frequency, Start with Windows, reset position, language, updates, and exit
|
||||
- Left-click the tray icon to toggle the taskbar widget on or off
|
||||
- Enable `Start with Windows` from the right-click menu if you want it to launch automatically when you sign in
|
||||
- Enable `Show detailed remaining time` under right-click `Settings` to add minutes alongside hours (5h window) and hours alongside days (7d window)
|
||||
- Enable `Start with Windows` from the right-click menu if you want it to launch automatically when you sign in
|
||||
|
||||
### Models
|
||||
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "Instellingen",
|
||||
start_with_windows: "Opstarten met Windows",
|
||||
reset_position: "Positie herstellen",
|
||||
show_detailed_remaining: "Gedetailleerde resterende tijd tonen",
|
||||
language: "Taal",
|
||||
system_default: "Systeemstandaard",
|
||||
check_for_updates: "Controleren op updates",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Update beschikbaar",
|
||||
update_prompt_now: "Versie {version} is beschikbaar. Wil je nu bijwerken?",
|
||||
exit: "Afsluiten",
|
||||
show_detailed_remaining: "Gedetailleerde resterende tijd tonen",
|
||||
show_widget: "Widget tonen",
|
||||
session_window: "5u",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "Settings",
|
||||
start_with_windows: "Start with Windows",
|
||||
reset_position: "Reset Position",
|
||||
show_detailed_remaining: "Show detailed remaining time",
|
||||
language: "Language",
|
||||
system_default: "System Default",
|
||||
check_for_updates: "Check for Updates",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Update available",
|
||||
update_prompt_now: "Version {version} is available. Do you want to update now?",
|
||||
exit: "Exit",
|
||||
show_detailed_remaining: "Show detailed remaining time",
|
||||
show_widget: "Show Widget",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "Paramètres",
|
||||
start_with_windows: "Démarrer avec Windows",
|
||||
reset_position: "Réinitialiser la position",
|
||||
show_detailed_remaining: "Afficher le temps restant détaillé",
|
||||
language: "Langue",
|
||||
system_default: "Par défaut du système",
|
||||
check_for_updates: "Vérifier les mises à jour",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Mise à jour disponible",
|
||||
update_prompt_now: "La version {version} est disponible. Voulez-vous mettre à jour maintenant ?",
|
||||
exit: "Quitter",
|
||||
show_detailed_remaining: "Afficher le temps restant détaillé",
|
||||
show_widget: "Afficher le widget",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "Einstellungen",
|
||||
start_with_windows: "Mit Windows starten",
|
||||
reset_position: "Position zurücksetzen",
|
||||
show_detailed_remaining: "Detaillierte Restzeit anzeigen",
|
||||
language: "Sprache",
|
||||
system_default: "Systemstandard",
|
||||
check_for_updates: "Nach Updates suchen",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Update verfügbar",
|
||||
update_prompt_now: "Version {version} ist verfügbar. Möchten Sie jetzt aktualisieren?",
|
||||
exit: "Beenden",
|
||||
show_detailed_remaining: "Detaillierte Restzeit anzeigen",
|
||||
show_widget: "Widget anzeigen",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "設定",
|
||||
start_with_windows: "Windows と同時に開始",
|
||||
reset_position: "位置をリセット",
|
||||
show_detailed_remaining: "残り時間を詳細表示",
|
||||
language: "言語",
|
||||
system_default: "システム既定",
|
||||
check_for_updates: "更新を確認",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "更新が利用可能です",
|
||||
update_prompt_now: "バージョン {version} が利用可能です。今すぐ更新しますか?",
|
||||
exit: "終了",
|
||||
show_detailed_remaining: "残り時間を詳細表示",
|
||||
show_widget: "ウィジェットを表示",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "설정",
|
||||
start_with_windows: "Windows 시작 시 자동 실행",
|
||||
reset_position: "위치 초기화",
|
||||
show_detailed_remaining: "남은 시간 상세 표시",
|
||||
language: "언어",
|
||||
system_default: "시스템 기본값",
|
||||
check_for_updates: "업데이트 확인",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "업데이트 사용 가능",
|
||||
update_prompt_now: "버전 {version}을 사용할 수 있습니다. 지금 업데이트하시겠습니까?",
|
||||
exit: "종료",
|
||||
show_detailed_remaining: "남은 시간 상세 표시",
|
||||
show_widget: "위젯 표시",
|
||||
session_window: "5시간",
|
||||
weekly_window: "7일",
|
||||
|
||||
@ -150,7 +150,6 @@ pub struct Strings {
|
||||
pub settings: &'static str,
|
||||
pub start_with_windows: &'static str,
|
||||
pub reset_position: &'static str,
|
||||
pub show_detailed_remaining: &'static str,
|
||||
pub language: &'static str,
|
||||
pub system_default: &'static str,
|
||||
pub check_for_updates: &'static str,
|
||||
@ -165,6 +164,7 @@ pub struct Strings {
|
||||
pub update_available: &'static str,
|
||||
pub update_prompt_now: &'static str,
|
||||
pub exit: &'static str,
|
||||
pub show_detailed_remaining: &'static str,
|
||||
pub show_widget: &'static str,
|
||||
pub session_window: &'static str,
|
||||
pub weekly_window: &'static str,
|
||||
|
||||
@ -30,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Atualização disponível",
|
||||
update_prompt_now: "Versão {version} está disponível. Deseja atualizar agora?",
|
||||
exit: "Sair",
|
||||
show_detailed_remaining: "Mostrar tempo restante detalhado",
|
||||
show_widget: "Exibir Widget",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
@ -43,5 +44,4 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
codex_token_expired_title: "Erro de Autenticação do Codex",
|
||||
codex_token_expired_body: "Execute 'codex' em um terminal e siga as instruções de login. Depois disso, atualize ou reinicie este aplicativo.",
|
||||
codex_window_title: "Monitor de uso do Codex",
|
||||
show_detailed_remaining: "Mostrar tempo restante detalhado",
|
||||
};
|
||||
|
||||
@ -30,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Доступно обновление",
|
||||
update_prompt_now: "Доступна версия {version}. Обновить сейчас?",
|
||||
exit: "Выход",
|
||||
show_detailed_remaining: "Показывать подробное оставшееся время",
|
||||
show_widget: "Показать виджет",
|
||||
session_window: "5ч",
|
||||
weekly_window: "7д",
|
||||
@ -43,5 +44,4 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
codex_token_expired_title: "Ошибка авторизации Codex",
|
||||
codex_token_expired_body: "Запустите 'codex' в терминале и следуйте инструкциям для входа. После этого обновите или перезапустите приложение.",
|
||||
codex_window_title: "Монитор использования Codex",
|
||||
show_detailed_remaining: "Показывать подробное оставшееся время",
|
||||
};
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "Configuración",
|
||||
start_with_windows: "Iniciar con Windows",
|
||||
reset_position: "Restablecer posición",
|
||||
show_detailed_remaining: "Mostrar tiempo restante detallado",
|
||||
language: "Idioma",
|
||||
system_default: "Predeterminado del sistema",
|
||||
check_for_updates: "Buscar actualizaciones",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "Actualización disponible",
|
||||
update_prompt_now: "La versión {version} está disponible. ¿Quieres actualizar ahora?",
|
||||
exit: "Salir",
|
||||
show_detailed_remaining: "Mostrar tiempo restante detallado",
|
||||
show_widget: "Mostrar widget",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -16,7 +16,6 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
settings: "設定",
|
||||
start_with_windows: "開機時啟動",
|
||||
reset_position: "重置位置",
|
||||
show_detailed_remaining: "顯示詳細剩餘時間",
|
||||
language: "語言",
|
||||
system_default: "系統預設",
|
||||
check_for_updates: "檢查更新",
|
||||
@ -31,6 +30,7 @@ pub(super) const STRINGS: Strings = Strings {
|
||||
update_available: "有可用更新",
|
||||
update_prompt_now: "版本 {version} 已可用。是否立即更新?",
|
||||
exit: "結束",
|
||||
show_detailed_remaining: "顯示詳細剩餘時間",
|
||||
show_widget: "顯示小工具",
|
||||
session_window: "5h",
|
||||
weekly_window: "7d",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde::Deserialize;
|
||||
@ -1020,10 +1021,25 @@ fn is_leap(y: u64) -> bool {
|
||||
(y % 4 == 0 && y % 100 != 0) || y % 400 == 0
|
||||
}
|
||||
|
||||
/// Detailed remaining-time flag, mirrored from window state. Read lock-free
|
||||
/// here so the countdown formatters (which run while the window state lock is
|
||||
/// held) never re-lock shared state. Kept at base signatures so other features
|
||||
/// that call these formatters stay source-compatible.
|
||||
static DETAILED_REMAINING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Update the detailed remaining-time flag the formatters read.
|
||||
pub fn set_detailed_remaining(enabled: bool) {
|
||||
DETAILED_REMAINING.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn detailed_remaining_enabled() -> bool {
|
||||
DETAILED_REMAINING.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Format a usage section as "X% · Yh" style text
|
||||
pub fn format_line(section: &UsageSection, strings: Strings, detailed: bool) -> String {
|
||||
pub fn format_line(section: &UsageSection, strings: Strings) -> String {
|
||||
let pct = format!("{:.0}%", section.percentage);
|
||||
let cd = format_countdown(section.resets_at, strings, detailed);
|
||||
let cd = format_countdown(section.resets_at, strings);
|
||||
if cd.is_empty() {
|
||||
pct
|
||||
} else {
|
||||
@ -1031,7 +1047,7 @@ pub fn format_line(section: &UsageSection, strings: Strings, detailed: bool) ->
|
||||
}
|
||||
}
|
||||
|
||||
fn format_countdown(resets_at: Option<SystemTime>, strings: Strings, detailed: bool) -> String {
|
||||
fn format_countdown(resets_at: Option<SystemTime>, strings: Strings) -> String {
|
||||
let reset = match resets_at {
|
||||
Some(t) => t,
|
||||
None => return String::new(),
|
||||
@ -1042,23 +1058,18 @@ fn format_countdown(resets_at: Option<SystemTime>, strings: Strings, detailed: b
|
||||
Err(_) => return strings.now.to_string(),
|
||||
};
|
||||
|
||||
format_countdown_from_secs(remaining.as_secs(), strings, detailed)
|
||||
format_countdown_from_secs(remaining.as_secs(), strings)
|
||||
}
|
||||
|
||||
/// Calculate how long until the display text would change
|
||||
pub fn time_until_display_change(
|
||||
resets_at: Option<SystemTime>,
|
||||
detailed: bool,
|
||||
) -> Option<Duration> {
|
||||
pub fn time_until_display_change(resets_at: Option<SystemTime>) -> Option<Duration> {
|
||||
let reset = resets_at?;
|
||||
let remaining = reset.duration_since(SystemTime::now()).ok()?;
|
||||
Some(time_until_display_change_from_secs(
|
||||
remaining.as_secs(),
|
||||
detailed,
|
||||
))
|
||||
Some(time_until_display_change_from_secs(remaining.as_secs()))
|
||||
}
|
||||
|
||||
fn format_countdown_from_secs(total_secs: u64, strings: Strings, detailed: bool) -> String {
|
||||
fn format_countdown_from_secs(total_secs: u64, strings: Strings) -> String {
|
||||
let detailed = detailed_remaining_enabled();
|
||||
let total_mins = total_secs / 60;
|
||||
let total_hours = total_secs / 3600;
|
||||
let total_days = total_secs / 86400;
|
||||
@ -1090,7 +1101,8 @@ fn format_countdown_from_secs(total_secs: u64, strings: Strings, detailed: bool)
|
||||
}
|
||||
}
|
||||
|
||||
fn time_until_display_change_from_secs(total_secs: u64, detailed: bool) -> Duration {
|
||||
fn time_until_display_change_from_secs(total_secs: u64) -> Duration {
|
||||
let detailed = detailed_remaining_enabled();
|
||||
let total_mins = total_secs / 60;
|
||||
let total_hours = total_secs / 3600;
|
||||
let total_days = total_secs / 86400;
|
||||
|
||||
131
src/window.rs
131
src/window.rs
@ -1,5 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
@ -421,22 +421,21 @@ fn refresh_usage_texts(state: &mut AppState) {
|
||||
}
|
||||
|
||||
let strings = state.language.strings();
|
||||
let detailed = state.show_detailed_remaining;
|
||||
let Some(data) = state.data.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(claude_code) = data.claude_code.as_ref() {
|
||||
state.session_text = poller::format_line(&claude_code.session, strings, detailed);
|
||||
state.weekly_text = poller::format_line(&claude_code.weekly, strings, detailed);
|
||||
state.session_text = poller::format_line(&claude_code.session, strings);
|
||||
state.weekly_text = poller::format_line(&claude_code.weekly, strings);
|
||||
} else if state.show_claude_code {
|
||||
state.session_text = "!".to_string();
|
||||
state.weekly_text = "!".to_string();
|
||||
}
|
||||
|
||||
if let Some(codex) = data.codex.as_ref() {
|
||||
state.codex_session_text = poller::format_line(&codex.session, strings, detailed);
|
||||
state.codex_weekly_text = poller::format_line(&codex.weekly, strings, detailed);
|
||||
state.codex_session_text = poller::format_line(&codex.session, strings);
|
||||
state.codex_weekly_text = poller::format_line(&codex.weekly, strings);
|
||||
} else if state.show_codex {
|
||||
state.codex_session_text = "!".to_string();
|
||||
state.codex_weekly_text = "!".to_string();
|
||||
@ -834,12 +833,31 @@ fn active_model_count(show_claude_code: bool, show_codex: bool) -> i32 {
|
||||
(show_claude_code as i32 + show_codex as i32).max(1)
|
||||
}
|
||||
|
||||
fn text_width_for(detailed: bool) -> i32 {
|
||||
if detailed {
|
||||
TEXT_WIDTH_DETAILED
|
||||
} else {
|
||||
TEXT_WIDTH
|
||||
}
|
||||
// Optional display features may widen the usage text column. Each feature
|
||||
// stores its own extra width (in base 96-DPI px) in a dedicated slot, read
|
||||
// lock-free by the width/layout helpers below. Slots are summed so multiple
|
||||
// features stack. This block is identical across the display-feature branches
|
||||
// so they merge without conflict; a branch only wires up its own slot.
|
||||
#[allow(dead_code)]
|
||||
static EXTRA_TEXT_WIDTH_DETAILED: AtomicI32 = AtomicI32::new(0);
|
||||
#[allow(dead_code)]
|
||||
static EXTRA_TEXT_WIDTH_ETD: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn set_extra_text_width_detailed(px: i32) {
|
||||
EXTRA_TEXT_WIDTH_DETAILED.store(px, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn set_extra_text_width_etd(px: i32) {
|
||||
EXTRA_TEXT_WIDTH_ETD.store(px, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Usage text-column width including any enabled display-feature widening.
|
||||
fn effective_text_width() -> i32 {
|
||||
TEXT_WIDTH
|
||||
+ EXTRA_TEXT_WIDTH_DETAILED.load(Ordering::Relaxed)
|
||||
+ EXTRA_TEXT_WIDTH_ETD.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn row_bar_segment_count(active_models: i32) -> i32 {
|
||||
@ -850,11 +868,20 @@ fn row_bar_segment_count(active_models: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn total_widget_width_for(active_models: i32, detailed: bool) -> i32 {
|
||||
/// Whether the detailed remaining-time display is enabled, read from shared
|
||||
/// state. Returns false when state is not yet populated (startup) or the lock
|
||||
/// cannot be acquired. Callers must not hold the state lock.
|
||||
fn show_detailed_remaining_enabled() -> bool {
|
||||
lock_state()
|
||||
.as_ref()
|
||||
.map_or(false, |s| s.show_detailed_remaining)
|
||||
}
|
||||
|
||||
fn total_widget_width_for(active_models: i32) -> i32 {
|
||||
let bar_segments = row_bar_segment_count(active_models);
|
||||
let model_width = (sc(SEGMENT_W) + sc(SEGMENT_GAP)) * bar_segments - sc(SEGMENT_GAP)
|
||||
+ sc(BAR_RIGHT_MARGIN)
|
||||
+ sc(text_width_for(detailed));
|
||||
+ sc(effective_text_width());
|
||||
|
||||
sc(LEFT_DIVIDER_W)
|
||||
+ sc(DIVIDER_RIGHT_MARGIN)
|
||||
@ -865,27 +892,15 @@ fn total_widget_width_for(active_models: i32, detailed: bool) -> i32 {
|
||||
+ sc(RIGHT_MARGIN)
|
||||
}
|
||||
|
||||
fn total_widget_width_for_state(state: &AppState) -> i32 {
|
||||
total_widget_width_for(
|
||||
active_model_count(state.show_claude_code, state.show_codex),
|
||||
state.show_detailed_remaining,
|
||||
)
|
||||
}
|
||||
|
||||
fn total_widget_width() -> i32 {
|
||||
let (active_models, detailed) = {
|
||||
let active_models = {
|
||||
let state = lock_state();
|
||||
state
|
||||
.as_ref()
|
||||
.map(|s| {
|
||||
(
|
||||
active_model_count(s.show_claude_code, s.show_codex),
|
||||
s.show_detailed_remaining,
|
||||
)
|
||||
})
|
||||
.unwrap_or((1, false))
|
||||
.map(|s| active_model_count(s.show_claude_code, s.show_codex))
|
||||
.unwrap_or(1)
|
||||
};
|
||||
total_widget_width_for(active_models, detailed)
|
||||
total_widget_width_for(active_models)
|
||||
}
|
||||
|
||||
fn claude_accent_color() -> Color {
|
||||
@ -986,7 +1001,7 @@ pub fn run() {
|
||||
WS_POPUP,
|
||||
0,
|
||||
0,
|
||||
total_widget_width_for(initial_model_count, settings.show_detailed_remaining),
|
||||
total_widget_width_for(initial_model_count),
|
||||
sc(WIDGET_HEIGHT),
|
||||
HWND::default(),
|
||||
HMENU::default(),
|
||||
@ -1058,6 +1073,15 @@ pub fn run() {
|
||||
});
|
||||
}
|
||||
|
||||
// Mirror the detailed-remaining flag into the poller, which reads it
|
||||
// lock-free while formatting countdowns (avoids re-locking shared state).
|
||||
poller::set_detailed_remaining(settings.show_detailed_remaining);
|
||||
set_extra_text_width_detailed(if settings.show_detailed_remaining {
|
||||
TEXT_WIDTH_DETAILED - TEXT_WIDTH
|
||||
} else {
|
||||
0
|
||||
});
|
||||
|
||||
// Try to embed in taskbar
|
||||
if let Some(taskbar_hwnd) = native_interop::find_taskbar() {
|
||||
diagnose::log(format!("taskbar found hwnd={:?}", taskbar_hwnd));
|
||||
@ -1179,7 +1203,6 @@ fn render_layered() {
|
||||
codex_weekly_text,
|
||||
show_claude_code,
|
||||
show_codex,
|
||||
show_detailed_remaining,
|
||||
) = {
|
||||
let state = lock_state();
|
||||
match state.as_ref() {
|
||||
@ -1198,7 +1221,6 @@ fn render_layered() {
|
||||
s.codex_weekly_text.clone(),
|
||||
s.show_claude_code,
|
||||
s.show_codex,
|
||||
s.show_detailed_remaining,
|
||||
),
|
||||
None => return,
|
||||
}
|
||||
@ -1289,7 +1311,6 @@ fn render_layered() {
|
||||
show_claude_code,
|
||||
show_codex,
|
||||
&codex_accent,
|
||||
show_detailed_remaining,
|
||||
);
|
||||
|
||||
// Background pixels → alpha 1 (nearly invisible but still hittable for right-click).
|
||||
@ -1360,7 +1381,6 @@ fn paint_content(
|
||||
show_claude_code: bool,
|
||||
show_codex: bool,
|
||||
codex_accent: &Color,
|
||||
detailed: bool,
|
||||
) {
|
||||
unsafe {
|
||||
let client_rect = RECT {
|
||||
@ -1453,7 +1473,6 @@ fn paint_content(
|
||||
accent,
|
||||
codex_accent,
|
||||
track,
|
||||
detailed,
|
||||
);
|
||||
draw_row(
|
||||
hdc,
|
||||
@ -1471,7 +1490,6 @@ fn paint_content(
|
||||
accent,
|
||||
codex_accent,
|
||||
track,
|
||||
detailed,
|
||||
);
|
||||
|
||||
SelectObject(hdc, old_font);
|
||||
@ -1662,20 +1680,19 @@ fn schedule_countdown_timer() {
|
||||
}
|
||||
}
|
||||
|
||||
let detailed = s.show_detailed_remaining;
|
||||
let delays = [
|
||||
data.claude_code
|
||||
.as_ref()
|
||||
.and_then(|usage| poller::time_until_display_change(usage.session.resets_at, detailed)),
|
||||
.and_then(|usage| poller::time_until_display_change(usage.session.resets_at)),
|
||||
data.claude_code
|
||||
.as_ref()
|
||||
.and_then(|usage| poller::time_until_display_change(usage.weekly.resets_at, detailed)),
|
||||
.and_then(|usage| poller::time_until_display_change(usage.weekly.resets_at)),
|
||||
data.codex
|
||||
.as_ref()
|
||||
.and_then(|usage| poller::time_until_display_change(usage.session.resets_at, detailed)),
|
||||
.and_then(|usage| poller::time_until_display_change(usage.session.resets_at)),
|
||||
data.codex
|
||||
.as_ref()
|
||||
.and_then(|usage| poller::time_until_display_change(usage.weekly.resets_at, detailed)),
|
||||
.and_then(|usage| poller::time_until_display_change(usage.weekly.resets_at)),
|
||||
];
|
||||
let min_delay = delays.into_iter().flatten().min();
|
||||
|
||||
@ -2046,6 +2063,9 @@ unsafe extern "system" fn wnd_proc(
|
||||
if is_dragging {
|
||||
let mut pt = POINT::default();
|
||||
let _ = GetCursorPos(&mut pt);
|
||||
// Compute the widget width before locking; the width helpers read
|
||||
// shared state themselves and must not run while the lock is held.
|
||||
let widget_width = total_widget_width();
|
||||
let move_target = {
|
||||
let mut state = lock_state();
|
||||
let s = match state.as_mut() {
|
||||
@ -2079,7 +2099,6 @@ unsafe extern "system" fn wnd_proc(
|
||||
tray_left = tray_rect.left;
|
||||
}
|
||||
}
|
||||
let widget_width = total_widget_width_for_state(s);
|
||||
let max_offset = (tray_left - taskbar_rect.left - widget_width).max(0);
|
||||
if new_offset > max_offset {
|
||||
new_offset = max_offset;
|
||||
@ -2257,6 +2276,12 @@ unsafe extern "system" fn wnd_proc(
|
||||
let mut state = lock_state();
|
||||
if let Some(s) = state.as_mut() {
|
||||
s.show_detailed_remaining = !s.show_detailed_remaining;
|
||||
poller::set_detailed_remaining(s.show_detailed_remaining);
|
||||
set_extra_text_width_detailed(if s.show_detailed_remaining {
|
||||
TEXT_WIDTH_DETAILED - TEXT_WIDTH
|
||||
} else {
|
||||
0
|
||||
});
|
||||
refresh_usage_texts(s);
|
||||
}
|
||||
}
|
||||
@ -2378,7 +2403,6 @@ fn show_context_menu(hwnd: HWND) {
|
||||
widget_visible,
|
||||
show_claude_code,
|
||||
show_codex,
|
||||
show_detailed_remaining,
|
||||
) = {
|
||||
let state = lock_state();
|
||||
match state.as_ref() {
|
||||
@ -2392,7 +2416,6 @@ fn show_context_menu(hwnd: HWND) {
|
||||
s.widget_visible,
|
||||
s.show_claude_code,
|
||||
s.show_codex,
|
||||
s.show_detailed_remaining,
|
||||
),
|
||||
None => (
|
||||
POLL_15_MIN,
|
||||
@ -2404,7 +2427,6 @@ fn show_context_menu(hwnd: HWND) {
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
}
|
||||
};
|
||||
@ -2511,7 +2533,7 @@ fn show_context_menu(hwnd: HWND) {
|
||||
);
|
||||
|
||||
let detailed_str = native_interop::wide_str(strings.show_detailed_remaining);
|
||||
let detailed_flags = if show_detailed_remaining {
|
||||
let detailed_flags = if show_detailed_remaining_enabled() {
|
||||
MF_CHECKED
|
||||
} else {
|
||||
MENU_ITEM_FLAGS(0)
|
||||
@ -2646,7 +2668,6 @@ fn paint(hdc: HDC, hwnd: HWND) {
|
||||
codex_weekly_text,
|
||||
show_claude_code,
|
||||
show_codex,
|
||||
show_detailed_remaining,
|
||||
) = {
|
||||
let state = lock_state();
|
||||
match state.as_ref() {
|
||||
@ -2663,7 +2684,6 @@ fn paint(hdc: HDC, hwnd: HWND) {
|
||||
s.codex_weekly_text.clone(),
|
||||
s.show_claude_code,
|
||||
s.show_codex,
|
||||
s.show_detailed_remaining,
|
||||
),
|
||||
None => return,
|
||||
}
|
||||
@ -2722,7 +2742,6 @@ fn paint(hdc: HDC, hwnd: HWND) {
|
||||
show_claude_code,
|
||||
show_codex,
|
||||
&codex_accent,
|
||||
show_detailed_remaining,
|
||||
);
|
||||
|
||||
let _ = BitBlt(hdc, 0, 0, width, height, mem_dc, 0, 0, SRCCOPY);
|
||||
@ -2749,7 +2768,6 @@ fn draw_row(
|
||||
claude_accent: &Color,
|
||||
codex_accent: &Color,
|
||||
track: &Color,
|
||||
detailed: bool,
|
||||
) {
|
||||
let seg_h = sc(SEGMENT_H);
|
||||
let active_models = active_model_count(show_claude_code, show_codex);
|
||||
@ -2794,9 +2812,8 @@ fn draw_row(
|
||||
claude_accent,
|
||||
track,
|
||||
&claude_value_color,
|
||||
detailed,
|
||||
);
|
||||
model_x += model_usage_width(segment_count, detailed) + sc(MODEL_RIGHT_MARGIN);
|
||||
model_x += model_usage_width(segment_count) + sc(MODEL_RIGHT_MARGIN);
|
||||
}
|
||||
if show_codex {
|
||||
draw_usage_bar(
|
||||
@ -2809,16 +2826,15 @@ fn draw_row(
|
||||
codex_accent,
|
||||
track,
|
||||
&codex_value_color,
|
||||
detailed,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn model_usage_width(segment_count: i32, detailed: bool) -> i32 {
|
||||
fn model_usage_width(segment_count: i32) -> i32 {
|
||||
(sc(SEGMENT_W) + sc(SEGMENT_GAP)) * segment_count - sc(SEGMENT_GAP)
|
||||
+ sc(BAR_RIGHT_MARGIN)
|
||||
+ sc(text_width_for(detailed))
|
||||
+ sc(effective_text_width())
|
||||
}
|
||||
|
||||
fn draw_usage_bar(
|
||||
@ -2831,7 +2847,6 @@ fn draw_usage_bar(
|
||||
accent: &Color,
|
||||
track: &Color,
|
||||
text_color: &Color,
|
||||
detailed: bool,
|
||||
) {
|
||||
let seg_w = sc(SEGMENT_W);
|
||||
let seg_h = sc(SEGMENT_H);
|
||||
@ -2892,7 +2907,7 @@ fn draw_usage_bar(
|
||||
let mut text_rect = RECT {
|
||||
left: text_x,
|
||||
top: y,
|
||||
right: text_x + sc(text_width_for(detailed)),
|
||||
right: text_x + sc(effective_text_width()),
|
||||
bottom: y + seg_h,
|
||||
};
|
||||
let _ = SetTextColor(hdc, COLORREF(text_color.to_colorref()));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user