212f8e16d2
- Added WindowDragController to manage custom dragging of sub-windows. - Integrated dragging logic into EqWindow and PlWindow, allowing for smooth window movement. - Introduced docking logic to snap windows together and maintain their positions relative to each other. - Implemented persistence of window positions using SharedPreferences. - Enhanced AppDelegate to support screen bounds and mouse location retrieval for accurate window positioning. - Updated pubspec.yaml to include screen_retriever dependency. - Added comprehensive tests for dock controller and dock logic to ensure correct behavior during window interactions. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
127 lines
6.4 KiB
Markdown
127 lines
6.4 KiB
Markdown
# PLAN.md — Winamp Desktop (dartamp) work plan
|
|
|
|
Order: A -> B -> C -> D (confirmed). Docking uses a custom in-Dart drag loop.
|
|
Prefs window matches the full original tab set. Resumable across sessions; mark
|
|
items `[x]` as done.
|
|
|
|
## Phase A — Skin subsystem (TODO items 5, 3, 4)
|
|
|
|
### A1. Extract classic-skin code into a project-level package (task 5)
|
|
|
|
- [x] New package `dartamp_skin` at `packages/dartamp_skin/`.
|
|
- [x] Moved `classic_skin.dart`, `skin_loader.dart`, `bmp_decoder.dart`,
|
|
`skin_image.dart`, `skin_slider.dart`, `text_font.dart`, and
|
|
`assets/skins/base/` into the package; added barrel + pubspec +
|
|
analysis_options.
|
|
- [x] Moved skin-debugging tooling (`tool/`) and `text_font_test.dart` into the
|
|
package; added `base_skin_test.dart` (loadBaseSkin smoke test).
|
|
- [x] App keeps all window/UI logic; imports `package:dartamp_skin/...`;
|
|
`archive` dep moved to the package; app gained `path_provider` (for A2).
|
|
- [x] Verify: `dart format`, `flutter analyze` (app + package clean),
|
|
`flutter test` (7 tests green), `flutter build macos --debug` succeeds
|
|
and bundles `packages/dartamp_skin/assets/...`. Note: package assets
|
|
resolve at `packages/dartamp_skin/assets/skins/base/` in the bundle, so
|
|
`loadBaseSkin` uses that path.
|
|
|
|
### A2. Self-contained storage + .wsz import (task 3)
|
|
|
|
- [x] `SkinRegistry` in the package (`skin_registry.dart`): scans a directory for
|
|
extracted skins, holds a `manifest.json` per skin (name + import time),
|
|
`importArchive()` unzips a `.wsz` into `skins/<name>/` (flat top-level
|
|
files only), `loadSkin()` overlays on the bundled base. Base is NOT
|
|
extracted to disk: it stays bundled in app assets and is served directly
|
|
by `loadBaseSkin()` (registry lists it first), so a first-run extraction
|
|
is unnecessary. Imported skins never touch original Winamp files.
|
|
- [x] App-side `SkinManager` (`lib/src/skin/skin_manager.dart`): owns the
|
|
registry + active skin + loaded `ClassicSkin`; `apply()`/`importArchive()`
|
|
persist `winamp.active_skin` and reload; sub-windows load their own skin
|
|
by name via `loadAppSkin()`.
|
|
- [x] Main window renders the manager's skin; EQ/PL sub-windows receive the skin
|
|
name in `SubWindowArgs` at spawn and live `setSkin` pushes over
|
|
`winamp.subwindows` control channel.
|
|
- [x] Import UI: "Load Skin…" item added to the app menu (MainMenu.xib +
|
|
AppDelegate `importSkin:`), wired to Dart `FilePicker` -> import + apply.
|
|
- [x] Verify: `flutter analyze` (app + package clean), package tests green
|
|
(11 incl. `skin_registry_test.dart`), `flutter build macos --debug` ok.
|
|
|
|
### A3. Cursors (task 4)
|
|
|
|
- [x] `CurDecoder` in the package (`cur_decoder.dart`): decodes Windows `.cur`
|
|
resources (1/4/8/32 bpp, AND-mask transparency, hotspot) into
|
|
`SkinCursor { ui.Image, hotspotX, hotspotY }`.
|
|
- [x] `ClassicSkin` exposes `Map<String, SkinCursor> cursors` keyed by the
|
|
`.cur` file stem; the base skin bundles the original
|
|
`normal.cur`/`move.cur`/`resize.cur` (copied from `original/Src/Winamp/
|
|
resource/`); `loadBaseSkin` + `_buildSkin` decode them, so imported skins
|
|
can override cursors and fall back to base otherwise.
|
|
- [x] App: `registerSkinCursors`/`setSkinCursor` over the `winamp` channel;
|
|
AppDelegate builds `NSCursor` from the PNG+hotspot and `setCursor`
|
|
switches it. `SkinCursorsGate` (MouseRegion) applies the skin's normal
|
|
cursor on enter and restores the arrow on exit, wrapping the main, EQ,
|
|
and PL windows.
|
|
- [x] Note: sub-windows run their own Flutter engines and their `winamp`
|
|
channel calls can't reach the AppDelegate handler, so custom cursors are
|
|
applied on the main window; EQ/PL keep the system arrow for now.
|
|
- [x] Verify: package tests (14 incl. `cur_decoder_test.dart`), app analyze
|
|
clean, `flutter build macos --debug` ok, app launches with no errors.
|
|
|
|
## Phase B — Docking & window management (new `lib/src/docking/`)
|
|
|
|
### B1. Plumbing
|
|
|
|
- [x] Persist per-window positions (`winamp.window_<name>_x/y` in PlayerState);
|
|
sub-windows spawn at saved positions instead of `center()`.
|
|
- [x] Sub-windows report bounds over `winamp.subwindows` (`subReady`/`subBounds`);
|
|
main holds the authoritative rect map; main -> sub pushes on demand
|
|
(`setDockContext`/`setBounds` on each sub-window's control channel).
|
|
- [x] Add `getScreenBounds` to AppDelegate's `winamp` channel.
|
|
- [x] Replace `DragSurface.startDragging()` with a custom drag loop
|
|
(`WindowDragSurface`/`WindowDragController`): pointer-down records start
|
|
pos + grab; move computes delta -> snap -> `setPosition`; dragged window
|
|
moves attached windows via relayed delta; drag-end persists positions +
|
|
recomputes attachments.
|
|
- [x] Unit tests for `dock_logic.dart` + `dock_controller.dart` (31 tests).
|
|
|
|
### B2. Dock logic (task 1) — port from DOCK.cpp / Set.cpp / ui.cpp
|
|
|
|
- [x] `IsWindowAttached` (dynamic geometry), `SnapWindowToWindow`, `SnapToScreen`,
|
|
`AdjustSnap`, `FixOverlaps` (`lib/src/docking/dock_logic.dart`).
|
|
- [x] Shade-toggle re-pin (`doMyDirtyShitholeDockingShit`) when main/EQ/PL sizes
|
|
change; docking severs automatically on window close.
|
|
- [ ] Verify vs original: stacked main->PL->EQ moves together; moving a docked
|
|
window undocks it; shade toggle keeps below-windows pinned; closing middle
|
|
PL severs the chain.
|
|
|
|
### B3. Playlist resize from bottom-right (task 6)
|
|
|
|
- Custom bottom-right resize handle (window_manager startResizing is
|
|
Win/Linux-only).
|
|
- Verify original row-snapping constraints; reuse re-pin for windows docked
|
|
below PL.
|
|
|
|
## Phase C — UI fixes
|
|
|
|
### C1. EQ presets window (task 7)
|
|
|
|
- Implement the presets window, wire the presets button, load/save to prefs.
|
|
|
|
### C2. PL bottom action buttons (task 8)
|
|
|
|
- Research original `button.bmp` regions + popup behavior; clipped sprites +
|
|
long-press full-sheet popup with release-to-activate; verify vs original.
|
|
|
|
## Phase D — Preferences + dock menu
|
|
|
|
### D1. Preferences window (task 2)
|
|
|
|
- Match original classic layout (Setup, Audio, Visualization, Plug-ins,
|
|
Skins, Titles, Directories, etc.); all tabs no-op except Skins (registry
|
|
list + active selection).
|
|
|
|
### D2. Dock icon menu (task 9)
|
|
|
|
- `applicationDockMenu(_:)` with Main/EQ/PL visibility toggles; add missing
|
|
main-window item to the Window menu; flip
|
|
`applicationShouldTerminateAfterLastWindowClosed` -> `false` so closing all
|
|
windows keeps the app alive.
|