# Changelog

All notable changes to myTk are documented here.

## [0.10.11] - 2026-02-26
### Fixed
- `TabularData._normalize_record()` no longer mutates `required_fields` on every insert
- `TabularData.record()` and `update_record()` now correctly look up records by UUID objects (not just strings)
- `SimpleDialog` key shortcuts (`<Return>`, `<Escape>`) now assigned after buttons are created, not before
- Several bugs fixed in `entries.py`: `Entry` binding order, `FormattedEntry` display initialisation, `FormattedEntry` crash on non-numeric input, `FormattedEntry.value` setter coercion, `CellEntry` focusout reference, `LabelledEntry` value passthrough
- `NumericEntry` consolidated into `IntEntry`; `NumericEntry` kept as alias for backwards compatibility

### Added
- `formatted_entry_app.py` example demonstrating `FormattedEntry` with various format strings

## [0.10.10] - 2026-02-25
### Changed
- `CanvasView.is_disabled` and `Dialog.is_disabled` now propagate disabled/enabled state to all descendant widgets

## [0.10.9] - 2026-02-25
### Changed
- `View.is_disabled` now recursively propagates the disabled/enabled state to all descendant widgets (same behaviour as `Box`)
- Refactored `_propagate_disabled()` helper from `Box` into `Base` to avoid duplication

## [0.10.8] - 2026-02-25
### Changed
- `Box.is_disabled` now recursively propagates the disabled/enabled state to all descendant widgets; disabling a `Box` grays out all contained controls automatically

## [0.10.7] - 2026-02-25
### Added
- `position` parameter on `App`, `Window`, and `Dialog` for named screen placement: `"center"`, `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"`
- `EventCapable._bind_destroy_cancel()`: automatically cancels all tracked `after()` tasks when a widget is destroyed; called by `grid_into`/`pack_into`/`place_into` as a safety net; convention is to also call it at the end of `create_widget()` overrides
- `_BaseWidget._bind_destroy_cancel()`: no-op fallback so subclasses can safely call it regardless of MRO
- `App.__init__` binds `<Destroy>` on root to cancel all pending Tcl `after`/`after_idle` events via `after info`

### Fixed
- `apply_window_position` deferred callback no longer causes `invalid command name` errors when the widget is destroyed before the `after_idle` fires

## [0.10.6] - 2026-02-24
### Fixed
- `XYPlot.create_widget` was assigning to `self.first_axis` which is a read-only computed property, causing `AttributeError` via `Bindable.__setattr__`; fix calls `self.figure.add_subplot()` directly (same pattern as `Histogram`)

## [0.10.5] - 2026-02-24
### Fixed
- `Histogram.create_widget` never added a subplot, leaving `first_axis` as `None` and causing a crash in `clear_plot()` / `update_plot()` when the histogram was updated (e.g. in `MicroscopeApp`)

## [0.10.4] - 2026-02-24
### Fixed
- `App(no_window=True)` now actually withdraws the main window — the parameter was previously accepted but silently ignored
- Removed stale `no_window=True` from `envtest.py`; tests need a visible window and the parameter had no effect before this fix

## [0.10.3] - 2026-02-18
### Added
- `progressbar_app.py` example demonstrating three ways to update a `ProgressBar`: direct value assignment, `step()`, and background thread via `schedule_on_main_thread()`

## [0.10.2] - 2026-02-18
### Added
- `bring_to_front=True` option on `App` — uses `osascript` on macOS to reliably activate the process window, overcoming editor focus reclaim

## [0.10.1] - 2026-02-17
### Fixed
- `ProgressBar` and `ProgressWindow` fully implemented and tested
- `ProgressBar.step()` now updates `value_variable` directly (bypasses unreliable `ttk.Progressbar.step()` sync)
- `ProgressWindow` exposes `self.progress_bar` after `run()`
- Notification handlers guard against stale observers with `try/except` around `winfo_exists()`

## [0.9.15] - 2026-02-14
### Added
- `Configurable` mixin and `ConfigModel` class for declarative property management with automatic validation and dialog generation (`ConfigurableNumericProperty`, `ConfigurableStringProperty`, `ConfigurationDialog`)

### Fixed
- Mutation-during-iteration bug in `after_cancel_all()`
- `Entry.__init__` was resetting initial value to empty string
- Wildcard re-export and missing `partial` import
- Hanging test in `testCustomDialogs`
- Dead code, hardcoded path, and optional `cv2` import
- Modernized project structure to current Python packaging standards

## [0.9.12] - 2025-xx-xx
### Added
- `schedule_on_main_thread()` on `App` for safely dispatching work from background threads
- `is_main_thread()` utility function
