55c396b2a2
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
5.0 KiB
5.0 KiB
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)
- New package
dartamp_skinatpackages/dartamp_skin/. - Moved
classic_skin.dart,skin_loader.dart,bmp_decoder.dart,skin_image.dart,skin_slider.dart,text_font.dart, andassets/skins/base/into the package; added barrel + pubspec + analysis_options. - Moved skin-debugging tooling (
tool/) andtext_font_test.dartinto the package; addedbase_skin_test.dart(loadBaseSkin smoke test). - App keeps all window/UI logic; imports
package:dartamp_skin/...;archivedep moved to the package; app gainedpath_provider(for A2). - Verify:
dart format,flutter analyze(app + package clean),flutter test(7 tests green),flutter build macos --debugsucceeds and bundlespackages/dartamp_skin/assets/.... Note: package assets resolve atpackages/dartamp_skin/assets/skins/base/in the bundle, soloadBaseSkinuses that path.
A2. Self-contained storage + .wsz import (task 3)
SkinRegistryin the package (skin_registry.dart): scans a directory for extracted skins, holds amanifest.jsonper skin (name + import time),importArchive()unzips a.wszintoskins/<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 byloadBaseSkin()(registry lists it first), so a first-run extraction is unnecessary. Imported skins never touch original Winamp files.- App-side
SkinManager(lib/src/skin/skin_manager.dart): owns the registry + active skin + loadedClassicSkin;apply()/importArchive()persistwinamp.active_skinand reload; sub-windows load their own skin by name vialoadAppSkin(). - Main window renders the manager's skin; EQ/PL sub-windows receive the skin
name in
SubWindowArgsat spawn and livesetSkinpushes overwinamp.subwindowscontrol channel. - Import UI: "Load Skin…" item added to the app menu (MainMenu.xib +
AppDelegate
importSkin:), wired to DartFilePicker-> import + apply. - Verify:
flutter analyze(app + package clean), package tests green (11 incl.skin_registry_test.dart),flutter build macos --debugok.
A3. Cursors (task 4)
ClassicSkinexposes cursor bitmaps (cursor.bmp/.cur) + hotspot (default skin included).- Native
NSCursorfrom skin bitmap via thewinampchannel; applied withMouseRegionover the windows.
Phase B — Docking & window management (new lib/src/docking/)
B1. Plumbing
- Persist per-window positions (
winamp.window_<name>_x/yin PlayerState); sub-windows spawn at saved positions instead ofcenter(). - Sub-windows report bounds over
winamp.subwindows; main holds the authoritative rect map; main -> sub pushes on demand. - Add
getScreenBoundsto AppDelegate'swinampchannel. - Replace
DragSurface.startDragging()with a custom drag loop: 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.
B2. Dock logic (task 1) — port from DOCK.cpp / Set.cpp / ui.cpp
IsWindowAttached(dynamic geometry),SnapWindowToWindow,SnapToScreen,AdjustSnap,FixOverlaps.- 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.bmpregions + 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; flipapplicationShouldTerminateAfterLastWindowClosed->falseso closing all windows keeps the app alive.