Skip to content

Releases

v1.3.2

Choose a tag to compare

@github-actions github-actions released this 12 Nov 14:18

Full Changelog: v1.3.1...v1.3.2

v1.3.1

Choose a tag to compare

@github-actions github-actions released this 20 Sep 15:09

What's Changed

  • fix(widgets): keep snapped widgets from cascading after minimize by @Vanchestery in #24

Full Changelog: v1.3.0...v1.3.1

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 16 Aug 15:33

What's Changed

  • feat: always-on-top setting, hide/show widgets, and restore snap/size by @Vanchestery in #20
  • docs: add current UI screenshots to README by @Vanchestery in #21

Full Changelog: v1.2.0...v1.3.0

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 24 Jun 20:08
b480b70

v1.2.0 — calculation & UX expansion

Шесть блоков фич плюс архитектурный фундамент под округление как первоклассный концепт ГСМ-учёта. Все блоки разбиты по Conventional Commits, каждый с отдельной историей.

Что нового

F. Контекстная команда процентов в стиле Windows Calc Standard. Кнопка ± заменена на %. Логика контекстная — зависит от ожидающего оператора:

  • 100 + 10 % → 100 + 10 = 110
  • 100 − 10 % → 90
  • 100 × 10 % → 10 (10% от 100)
  • 100 ÷ 10 % → 1000
  • 10 % без оператора → 0.1

Хоткей Shift+5. В Engineering-режиме % работает упрощённо (value/100), как Windows Calc Scientific. Цепочка после % корректно закрывается оператором (100 + 10% + 5 = 115) через флаг _justResolvedPercent.

G. Копирование значений в буфер обмена. Калькулятор: Ctrl+C + иконка-кнопка справа от дисплея + правый клик → контекстное меню. Копирует округлённое значение дисплея (по K). Виджет: то же UI, копирует только число из результата (без единиц).

Новый сервис IClipboardService с try/catch на COM-ошибки (буфер занят другим процессом — тихо не копируем, не падаем).

H. Авто-сохранение изменений виджета. Юзер двигает слайдер decimal places или меняет плотность — изменения автоматически пишутся в widgets.json через 500 мс после последнего изменения. Debounce защищает от мелкого спама на диск.

Новые абстракции IDebouncer / IDebouncerFactory (реализация на DispatcherTimer). Re-fetch виджета перед сохранением предотвращает race с одновременным редактированием через Add Widget. Flush при Dispose — последнее изменение не теряется при закрытии окна.

I. Закреплённые виджеты + панель в главном окне. Новая левая панель «Избранное» — зеркало панели истории, со своим тогглом в топ-баре (иконка ★). Клик по плитке открывает виджет.

Новый сервис IFavoritesService (Id-only, событие FavoritesChanged) + событие WidgetsChanged на IWidgetService. Закрепление через две точки: кнопка в Add Widget или чекбокс в форме редактирования. Удаление виджета автоматически убирает его из избранного. Состояние видимости панели сохраняется в window-state.json.

J. Магнитное прилипание виджетов к главному окну. Winamp-style: подвёл виджет на ~12 пикселей к грани главного — щёлк, прилип. Когда главное окно двигают — все прилипшие виджеты едут вместе. Отлепляется когда юзер тянет виджет дальше от грани.

Чистая логика в MagnetismCalculator (без WPF-зависимостей, юнит-тестируется). Сервис IWindowMagnetismService связывает калькулятор с LocationChanged событиями WPF. Флаг _isPositioningProgrammatically разрывает рекурсию при программном позиционировании.

fix: Учёт невидимых теней DWM через DWMWA_EXTENDED_FRAME_BOUNDS — без него был визуальный зазор ~14px между прилипшими окнами. Теперь pixel-perfect alignment, DPI-aware через HwndSource.TransformFromDevice.

K. Режим округления (первоклассный концепт). Циклическая кнопка в топ-баре переключает три режима: ∞ (без округления, default), 1 (до целых), 0.1 (до десятых). Применяется ко всему «наружному»: дисплей, аккумулятор калькулятора, история, вставка из виджета, копирование в буфер.

По К.5=Y (ГСМ-учётная семантика): каждый = округляет результат, и аккумулятор реально становится округлённым. Цепочка 100 + 50.7 = → 151 → + 0.4 = → 151 (а не 151.1). В Engineering округление только на финальном =.

Banker's rounding (MidpointRounding.ToEven) — стандарт IEEE 754 и финансовых расчётов, исключает систематический биас на больших объёмах округлений.

Архитектурные моменты

  • Чистая логика отдельно от WPF. RoundingFormatter, MagnetismCalculator — статические функции без зависимостей от System.Windows.Window (только от System.Windows.Rect / Thickness). Юнит-тестируется без UI thread.

  • События в сервисах для рефреша UI. IFavoritesService.FavoritesChanged и IWidgetService.WidgetsChanged — pub-sub, MainViewModel слушает оба. Цикл Add Widget делает edit → Service raises событие → MainViewModel перерисовывает Favorites работает без прямых ссылок между VM.

  • Update сервисов защищает инварианты. WidgetService.Update не даёт обнулить IsBuiltIn; IFavoritesService.Add идемпотентен (повторный вызов — no-op, не дубликаты).

  • Composition over inheritance для layout. ComputeMinWidth(favs, hist) — чистая функция, легко расширяется (если в v1.3 добавится 3-я панель).

Тесты

Добавлено ~70 новых тестов через все блоки:

Блок Тесты
F 9 CalculatorService.ResolvePercent + 11 MainViewModel percent UX
G 5 CopyDisplayCommand + 4 CopyResultCommand
H 9 WidgetViewModel auto-save (TestDebouncer для детерминизма)
I 13 FavoritesService + 6 MainViewModel favorites
J 14 MagnetismCalculator (4 грани + threshold edge cases + ambiguity tiebreak) + 5 insets
K 18 RoundingFormatter + 11 MainViewModel rounding behaviour

Все зелёные.

UX-улучшения помимо больших фич

  • Кнопка ± удалена (унарный минус почти не используется в ГСМ; место отдано под %).
  • Виджет ужат ранее в v1.1 — теперь компактнее и пиксель-перфектно прилипает к главному окну.
  • WrapPanel для кнопок в Add Widget — 5 кнопок не вылезают за окно при узком ресайзе.
  • Иконки топ-бара: ★ (избранное), ∞/1/0.1 (округление) — единообразно с Segoe MDL2 Assets.

Известные ограничения

  • Resize главного окна мышью НЕ двигает прилипшие виджеты (только перетаскивание за заголовок). LocationChanged не фаерится на ресайз, только SizeChanged. Можно поправить в v1.3, если будет нужно.
  • Если виджет переименовали через Add Widget пока он открыт — заголовок окна виджета остаётся со старым именем до перезагрузки. Известно с v1.1, не задача v1.2.

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 14 Jun 21:18
1a3d121

v1.1.0 — UX improvements after first user testing

После публикации v1.0 и первого теста "в бою" собрал пачку пожеланий и багов. Вот что вошло в v1.1.

Что нового

A. Фикс сброса дисплея после виджета.
После «Вставить в калькулятор» следующая нажатая цифра теперь корректно начинает новое число, а не дописывается к вставленному.

B. Тёмные надписи в списке виджетов.
Имена виджетов в «Добавить виджет» больше не сливаются с фоном на тёмной теме — явно задан Foreground=TextForeground, чтобы перебить стандартный SystemColors.ControlText из ListBoxItem.ControlTemplate.

C. Память позиции и размера главного окна.

  • Первый запуск — окно по центру экрана, размер Auto (SizeToContent=WidthAndHeight).
  • Позиция/размер запоминаются между сессиями в %AppData%/GsmCalculator/window-state.json — отдельно от session state (позиция помнится всегда, даже без явного сохранения сессии).
  • Восстановление учитывает multi-monitor: если сохранённая позиция вне экрана (ScreenHelper.IsOnScreen), окно центрируется заново.
  • Используется Window.RestoreBounds — корректно сохраняется размер Normal-state даже если окно было свернуто/развернуто.

D. Toggle панели истории + auto-размер.

  • Новая кнопка-иконка  в топ-баре прячет/показывает панель истории.
  • При скрытии окно сжимается до 360px (только калькулятор), при показе разворачивается до 540px минимум.
  • Состояние видимости истории сохраняется в MainWindowState.IsHistoryVisible.

E. Редактирование виджетов (включая встроенные).

  • В окне «Добавить виджет» появилась кнопка «Изменить» (доступна для любого виджета).
  • CreateWidgetWindow переиспользуется в двух режимах через optional Widget? toEdit — DRY поверх одних и тех же полей и валидации.
  • WidgetService.Update() защищает инвариант IsBuiltIn на уровне сервиса: даже если вызывающий код подсунет IsBuiltIn=false для встроенного виджета, флаг останется true. Тест явно проверяет это.

Бонус — иконки и компактность

  • Три кнопки топ-бара (+, настройки, история) заменены на иконки Segoe MDL2 Assets (E710, E713, E81C) с ToolTip — позволило сузить главное окно в компактном режиме.
  • Виджет ужат: ширина 300 → 215, высота кнопок и шрифты пропорционально уменьшены.

Технические детали

  • Новый сервис IWindowStateService + helper ScreenHelper.IsOnScreen (вынесен из WidgetWindowService).
  • MainViewModel.NotifyDisplayConsumed() — публичный API, дёргается из WidgetViewModel после GetDisplayValue.
  • Архитектура: physical window layout (MainWindowState) отделён от пользовательских настроек (AppSettings).

Тесты

Добавлено 13 новых тестов:

  • 5 для MainViewModel (display consume, history toggle, label localization)
  • 4 для WindowStateService (load missing/corrupt, round-trip, dir creation)
  • 4 для WidgetService.Update (preserve IsBuiltIn, persist, unknown id no-op, empty name throws)

Все зелёные.

What's Changed

New Contributors

Full Changelog: v1.0.0...v1.1.0

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 16 May 22:07