Migreer screen_retriever + window_manager naar nativeapi-flutter (uitfasering aangekondigd) #1741

Closed
opened 2026-08-23 08:37:02 +00:00 by brenno · 10 comments
Owner

Achtergrond

Beide packages van uitgever leanflutter.dev hebben een migratie-melding op pub.dev staan:

⚠️ Migration Notice: This plugin is being migrated to [libnativeapi/nativeapi-flutter]

De nieuwe versie is gebaseerd op een verenigde C++ kernbibliotheek (libnativeapi/nativeapi) die meer complete en consistente cross-platform native API-ondersteuning biedt.

De packages zijn nog actief (laatste updates 49 dagen geleden), maar de uitgever heeft aangekondigd dat de toekomst bij nativeapi-flutter ligt. Dit is een uitfaseringstraject.

Huidige situatie

screen_retriever (^0.2.0)

  • Direct dependency in pubspec.yaml
  • Gebruikt voor het ophalen van scherminformatie (grootte, displays, cursorpositie)
  • We hebben een vendored fork als override: third_party/screen_retriever_macos (via dependency_overrides)
    • Deze fork voegt native macOS window-geometry/fullscreen methods toe die de published 0.3.0 liet vallen
    • Nodig voor dual-screen presenter mode

window_manager (^0.5.1)

  • Direct dependency in pubspec.yaml
  • Gebruikt voor window management (resizen, repositioneren, title bar, fullscreen)
  • Hangt af van screen_retriever

Gebruik in OciDeck

screen_retriever wordt gebruikt in de presenter mode voor het bepalen van schermgrenzen en display-informatie. window_manager wordt gebruikt voor window-geometry, fullscreen mode en de title bar.

Beide packages raken de kern van de presentatie-ervaring op desktop (macOS, Linux, Windows).

Het alternatieft: nativeapi-flutter

libnativeapi/nativeapi-flutter is de officiële opvolger:

  • Zelfde uitgever (leanflutter.dev)
  • Gebaseerd op een C++ kernbibliotheek voor consistente cross-platform API
  • Bedoeld als verenigde vervanger voor zowel screen_retriever als window_manager

Wat dit issue moet uitzoeken

  1. Is nativeapi-flutter al productie-rijp? Het is mogelijk nog in ontwikkeling.
  2. Biedt het dezelfde functionaliteit als onze vendored screen_retriever_macos fork (window-geometry, fullscreen methods)?
  3. Hoeveel call-sites moeten aangepast worden?
  4. Kan de vendored fork verwijderd worden na migratie?

Aanpak

  1. Onderzoek nativeapi-flutter API en feature-pariteit
  2. Vergelijk met huidige screen_retriever + window_manager API-gebruik
  3. Controleer of de macOS-specifieke fork-methods gedekt zijn
  4. Plan de migratie in fasen (eerst screen_retriever, dan window_manager)
  5. Beeldkeuring op alle drie desktop-platformen (macOS, Linux, Windows) — presenter mode, fullscreen, dual-screen

Prioriteit

Niet urgent — de packages werken nog en krijgen nog updates. Maar de uitgever heeft de migratierichting aangekondigd, dus dit is technical debt die aangepakt moet worden voordat de packages stoppen met updates.

## Achtergrond Beide packages van uitgever leanflutter.dev hebben een **migratie-melding** op pub.dev staan: > ⚠️ Migration Notice: This plugin is being migrated to [libnativeapi/nativeapi-flutter] De nieuwe versie is gebaseerd op een verenigde C++ kernbibliotheek (`libnativeapi/nativeapi`) die meer complete en consistente cross-platform native API-ondersteuning biedt. De packages zijn nog actief (laatste updates 49 dagen geleden), maar de uitgever heeft aangekondigd dat de toekomst bij `nativeapi-flutter` ligt. Dit is een uitfaseringstraject. ## Huidige situatie ### `screen_retriever` (^0.2.0) - Direct dependency in pubspec.yaml - Gebruikt voor het ophalen van scherminformatie (grootte, displays, cursorpositie) - We hebben een **vendored fork** als override: `third_party/screen_retriever_macos` (via `dependency_overrides`) - Deze fork voegt native macOS window-geometry/fullscreen methods toe die de published 0.3.0 liet vallen - Nodig voor dual-screen presenter mode ### `window_manager` (^0.5.1) - Direct dependency in pubspec.yaml - Gebruikt voor window management (resizen, repositioneren, title bar, fullscreen) - Hangt af van `screen_retriever` ## Gebruik in OciDeck `screen_retriever` wordt gebruikt in de presenter mode voor het bepalen van schermgrenzen en display-informatie. `window_manager` wordt gebruikt voor window-geometry, fullscreen mode en de title bar. Beide packages raken de kern van de presentatie-ervaring op desktop (macOS, Linux, Windows). ## Het alternatieft: `nativeapi-flutter` `libnativeapi/nativeapi-flutter` is de officiële opvolger: - Zelfde uitgever (leanflutter.dev) - Gebaseerd op een C++ kernbibliotheek voor consistente cross-platform API - Bedoeld als verenigde vervanger voor zowel `screen_retriever` als `window_manager` ## Wat dit issue moet uitzoeken 1. Is `nativeapi-flutter` al productie-rijp? Het is mogelijk nog in ontwikkeling. 2. Biedt het dezelfde functionaliteit als onze vendored `screen_retriever_macos` fork (window-geometry, fullscreen methods)? 3. Hoeveel call-sites moeten aangepast worden? 4. Kan de vendored fork verwijderd worden na migratie? ## Aanpak 1. Onderzoek `nativeapi-flutter` API en feature-pariteit 2. Vergelijk met huidige `screen_retriever` + `window_manager` API-gebruik 3. Controleer of de macOS-specifieke fork-methods gedekt zijn 4. Plan de migratie in fasen (eerst screen_retriever, dan window_manager) 5. Beeldkeuring op alle drie desktop-platformen (macOS, Linux, Windows) — presenter mode, fullscreen, dual-screen ## Prioriteit Niet urgent — de packages werken nog en krijgen nog updates. Maar de uitgever heeft de migratierichting aangekondigd, dus dit is technical debt die aangepakt moet worden voordat de packages stoppen met updates.
Author
Owner

Welke licentie hoort hierbij?

Welke licentie hoort hierbij?
Author
Owner

Licentie- en taalonderzoek

Huidige packages

Package Licentie Taal Opmerking
screen_retriever MIT Dart, C++, C, Swift Platform-specifieke native code: C/C++ voor Windows/Linux, Swift voor macOS, Dart voor Flutter-interface.
window_manager MIT Dart, C++, C, Swift Zelfde architectuur als screen_retriever. Hangt af van screen_retriever.

Daarnaast hebben we een vendored fork in third_party/screen_retriever_macos die extra macOS-methods toevoegt.

Voorgestelde vervanging: nativeapi

Package Licentie Taal Opmerking
nativeapi MIT Dart, C++, Objective-C++, C, Kotlin, Swift Flutter bindings voor libnativeapi C++ kernbibliotheek. GitHub: libnativeapi/nativeapi-flutter. De C++ kern is 65.7% C++, 15.6% Objective-C++, 10.1% Python, 7.6% C.

Belangrijk: het pakket heet op pub.dev nativeapi (niet nativeapi-flutter — dat is de GitHub repo-naam).

Conclusie

Alle packages gebruiken MIT. De architectuur verschchuift van aparte platform-specifieke implementaties naar een verenigde C++ kernbibliotheek met Dart-erboven. De taalmix wordt breder (C++ kern + Objective-C++ voor Apple-platformen + Kotlin voor toekomstige Android-ondersteuning), maar dat is transparent voor onze Dart-code.

Let op: nativeapi staat nog in ontwikkeling ("work in progress"). De migratie is pas mogelijk als het pakket productie-rijp is en dezelfde macOS-methods dekt als onze vendored fork.

## Licentie- en taalonderzoek ### Huidige packages | Package | Licentie | Taal | Opmerking | |---|---|---|---| | **screen_retriever** | MIT | Dart, C++, C, Swift | Platform-specifieke native code: C/C++ voor Windows/Linux, Swift voor macOS, Dart voor Flutter-interface. | | **window_manager** | MIT | Dart, C++, C, Swift | Zelfde architectuur als screen_retriever. Hangt af van screen_retriever. | Daarnaast hebben we een **vendored fork** in `third_party/screen_retriever_macos` die extra macOS-methods toevoegt. ### Voorgestelde vervanging: nativeapi | Package | Licentie | Taal | Opmerking | |---|---|---|---| | **nativeapi** | MIT | Dart, C++, Objective-C++, C, Kotlin, Swift | Flutter bindings voor libnativeapi C++ kernbibliotheek. GitHub: libnativeapi/nativeapi-flutter. De C++ kern is 65.7% C++, 15.6% Objective-C++, 10.1% Python, 7.6% C. | **Belangrijk**: het pakket heet op pub.dev `nativeapi` (niet `nativeapi-flutter` — dat is de GitHub repo-naam). ### Conclusie Alle packages gebruiken **MIT**. De architectuur verschchuift van aparte platform-specifieke implementaties naar een verenigde C++ kernbibliotheek met Dart-erboven. De taalmix wordt breder (C++ kern + Objective-C++ voor Apple-platformen + Kotlin voor toekomstige Android-ondersteuning), maar dat is transparent voor onze Dart-code. **Let op**: `nativeapi` staat nog in ontwikkeling ("work in progress"). De migratie is pas mogelijk als het pakket productie-rijp is en dezelfde macOS-methods dekt als onze vendored fork.
Author
Owner

Antwoorden op de vier vragen

1. Is nativeapi al productie-rijp?

Nee. Het pakket staat expliciet op 0.1.4 en de README zegt: "🚧 Work in Progress: This package is currently under active development". Er zijn pas 6 versies gepubliceerd (0.0.1 t/m 0.1.4), geen 1.0.0, en de documentatie is incompleet ("More detailed documentation and examples are coming soon").

Conclusie: niet geschikt voor productie-gebruik. De migratie kan pas overwogen worden als er een stabiele 1.0.0 release is met volledige documentatie.

2. Biedt het dezelfde functionaliteit als onze vendored screen_retriever_macos fork?

Belangrijke correctie op het issue: de vendored fork in third_party/screen_retriever_macos voegt geen window-geometry of fullscreen methods toe. De fork bestaat uitsluitend om een SPM-bouwbaarheidsprobleem op te lossen — upstream 0.2.0 had alleen een CocoaPods Classes/-layout, terwijl recente Xcode/SPM een Sources/<target>/-layout verwacht. Zie MODIFICATIONS.md in de fork: er zijn twee bestanden toegevoegd (Package.swift + een SPM-layout kopie van de plugin source), verder is alles byte-identiek aan upstream.

De window-geometry methods (getBounds, setBounds) en fullscreen methods (setFullScreen) die we gebruiken zitten in window_manager, niet in screen_retriever.

Wat nativeapi betreft:

  • Screen/display retrieval: gedekt — DisplayManager.getAll(), cursor position, display info
  • Window geometry (getBounds/setBounds): gedekt in de C++ kern, cross-platform
  • Fullscreen (setFullScreen): onduidelijk — niet expliciet gedocumenteerd in de Flutter API. Er is een GitHub issue (#13) over fullscreen-gedrag met titleBarStyle, maar geen duidelijke setFullScreen-equivalent in de Flutter bindings

3. Hoeveel call-sites moeten aangepast worden?

screen_retriever — 2 call-sites:

  • lib/widgets/presentation/fullscreen_presenter.dart (regel 258): screenRetriever.getAllDisplays()
  • lib/widgets/presentation/parts/presenter_displays.dart (regel 8): screenRetriever.getAllDisplays()

Beide gebruiken alleen getAllDisplays(). Geen getPrimaryDisplay of getCursorScreenPoint in onze code.

window_manager — 7 call-sites in 4 bestanden:

  • lib/platform/native_window_io.dart: ensureInitialized(), waitUntilReadyToShow(), show(), focus(), setPreventClose(), destroy()
  • lib/platform/presenter_fullscreen_io.dart: setFullScreen()
  • lib/widgets/presentation/parts/presenter_displays.dart: getBounds(), setFullScreen(), setBounds()
  • lib/widgets/app_shell.dart: addListener(), removeListener(), destroy()

Totaal: 9 call-sites in 5 bestanden. Beperkt en overzichtelijk.

4. Kan de vendored fork verwijderd worden na migratie?

Ja, maar onafhankelijk van de nativeapi-migratie. De fork bestaat alleen voor SPM-bouwbaarheid. Upstream screen_retriever_macos heeft sinds 0.2.1+ zelf SPM-support gekregen (zoals vermeld in de MODIFICATIONS.md: "Upstream has since gained SPM support of its own (0.2.1+). When this fork is bumped, check whether it can be dropped entirely.").

De fork kan verwijderd worden door screen_retriever te bumpen naar 0.2.1+ en de dependency_overrides uit pubspec.yaml te halen — zodra geverifieerd is dat de upstream SPM-build werkt. Dat is een aparte, kleine opruimactie die niet op nativeapi hoeft te wachten.

Samenvatting en aanbeveling

Vraag Antwoord
Productie-rijp? Nee — 0.1.4, "Work in Progress", geen 1.0.0
Functionele dekking? Grotendeels ja, behalve fullscreen (onduidelijk)
Call-sites? 9 in 5 bestanden — beperkt
Vendored fork verwijderbaar? Ja, onafhankelijk — upstream heeft zelf SPM-support sinds 0.2.1+

Aanbeveling:

  1. Nu: verwijder de vendored screen_retriever_macos fork door screen_retriever te bumpen naar 0.2.2 (heeft SPM-support). Dat is een kleine opruimactie die de technical debt direct vermindert.
  2. Wachten: houd nativeapi in de gaten. Migreer pas als er een 1.0.0 release is met fullscreen-ondersteuning gedocumenteerd. Dit issue open houden als tracking-issue.
  3. De huidige screen_retriever + window_manager packages werken en krijgen nog updates. Geen urgentie.
## Antwoorden op de vier vragen ### 1. Is `nativeapi` al productie-rijp? **Nee.** Het pakket staat expliciet op **0.1.4** en de README zegt: "🚧 Work in Progress: This package is currently under active development". Er zijn pas 6 versies gepubliceerd (0.0.1 t/m 0.1.4), geen 1.0.0, en de documentatie is incompleet ("More detailed documentation and examples are coming soon"). **Conclusie**: niet geschikt voor productie-gebruik. De migratie kan pas overwogen worden als er een stabiele 1.0.0 release is met volledige documentatie. ### 2. Biedt het dezelfde functionaliteit als onze vendored `screen_retriever_macos` fork? **Belangrijke correctie op het issue**: de vendored fork in `third_party/screen_retriever_macos` voegt **geen** window-geometry of fullscreen methods toe. De fork bestaat uitsluitend om een **SPM-bouwbaarheidsprobleem** op te lossen — upstream 0.2.0 had alleen een CocoaPods `Classes/`-layout, terwijl recente Xcode/SPM een `Sources/<target>/`-layout verwacht. Zie `MODIFICATIONS.md` in de fork: er zijn twee bestanden toegevoegd (Package.swift + een SPM-layout kopie van de plugin source), verder is alles byte-identiek aan upstream. De window-geometry methods (`getBounds`, `setBounds`) en fullscreen methods (`setFullScreen`) die we gebruiken zitten in **`window_manager`**, niet in `screen_retriever`. Wat `nativeapi` betreft: - **Screen/display retrieval**: ✅ gedekt — `DisplayManager.getAll()`, cursor position, display info - **Window geometry** (`getBounds`/`setBounds`): ✅ gedekt in de C++ kern, cross-platform - **Fullscreen** (`setFullScreen`): ❓ onduidelijk — niet expliciet gedocumenteerd in de Flutter API. Er is een GitHub issue (#13) over fullscreen-gedrag met titleBarStyle, maar geen duidelijke `setFullScreen`-equivalent in de Flutter bindings ### 3. Hoeveel call-sites moeten aangepast worden? **`screen_retriever`** — 2 call-sites: - `lib/widgets/presentation/fullscreen_presenter.dart` (regel 258): `screenRetriever.getAllDisplays()` - `lib/widgets/presentation/parts/presenter_displays.dart` (regel 8): `screenRetriever.getAllDisplays()` Beide gebruiken alleen `getAllDisplays()`. Geen `getPrimaryDisplay` of `getCursorScreenPoint` in onze code. **`window_manager`** — 7 call-sites in 4 bestanden: - `lib/platform/native_window_io.dart`: `ensureInitialized()`, `waitUntilReadyToShow()`, `show()`, `focus()`, `setPreventClose()`, `destroy()` - `lib/platform/presenter_fullscreen_io.dart`: `setFullScreen()` - `lib/widgets/presentation/parts/presenter_displays.dart`: `getBounds()`, `setFullScreen()`, `setBounds()` - `lib/widgets/app_shell.dart`: `addListener()`, `removeListener()`, `destroy()` Totaal: **9 call-sites in 5 bestanden**. Beperkt en overzichtelijk. ### 4. Kan de vendored fork verwijderd worden na migratie? **Ja, maar onafhankelijk van de nativeapi-migratie.** De fork bestaat alleen voor SPM-bouwbaarheid. Upstream `screen_retriever_macos` heeft sinds **0.2.1+ zelf SPM-support** gekregen (zoals vermeld in de `MODIFICATIONS.md`: "Upstream has since gained SPM support of its own (0.2.1+). When this fork is bumped, check whether it can be dropped entirely."). De fork kan verwijderd worden door `screen_retriever` te bumpen naar 0.2.1+ en de `dependency_overrides` uit pubspec.yaml te halen — zodra geverifieerd is dat de upstream SPM-build werkt. Dat is een aparte, kleine opruimactie die niet op `nativeapi` hoeft te wachten. ## Samenvatting en aanbeveling | Vraag | Antwoord | |---|---| | Productie-rijp? | **Nee** — 0.1.4, "Work in Progress", geen 1.0.0 | | Functionele dekking? | Grotendeels ja, behalve fullscreen (onduidelijk) | | Call-sites? | 9 in 5 bestanden — beperkt | | Vendored fork verwijderbaar? | **Ja, onafhankelijk** — upstream heeft zelf SPM-support sinds 0.2.1+ | **Aanbeveling**: 1. **Nu**: verwijder de vendored `screen_retriever_macos` fork door `screen_retriever` te bumpen naar 0.2.2 (heeft SPM-support). Dat is een kleine opruimactie die de technical debt direct vermindert. 2. **Wachten**: houd `nativeapi` in de gaten. Migreer pas als er een 1.0.0 release is met fullscreen-ondersteuning gedocumenteerd. Dit issue open houden als tracking-issue. 3. De huidige `screen_retriever` + `window_manager` packages werken en krijgen nog updates. Geen urgentie.
Author
Owner

Testplan, definitie van 'goed genoeg', en wat bij problemen

Na bestudering van de daadwerkelijke nativeapi 0.1.4 Dart-API (uit de pub-cache) is hier de concrete mapping van onze 9 call-sites naar nativeapi-equivalenten, met de gaten expliciet benoemd.

API-mapping: wat we nodig hebben vs. wat nativeapi biedt

Onze call-site nativeapi-equivalent Status
screenRetriever.getAllDisplays() DisplayManager.instance.getAll() Geeft List<Display> met position, size, scaleFactor
display.visiblePosition / visibleSize display.position / display.size of display.workArea ⚠️ nativeapi heeft geen visiblePosition/visibleSize (die rekenen de menubar/taskbar weg). workArea is mogelijk het equivalent — moet getest worden.
windowManager.ensureInitialized() Niet nodig (FFI, geen plugin-registratie) Vermoedelijk
windowManager.waitUntilReadyToShow(options) Geen equivalent — opties per-call zetten ⚠️ Andere pattern: window.title = ..., window.setMinimumSize()
windowManager.show() window.show()
windowManager.focus() window.focus()
windowManager.setFullScreen(bool) window.isFullscreen = bool
windowManager.getBounds() window.bounds
windowManager.setBounds(Rect) window.bounds = Rect
windowManager.destroy() WindowManager.instance.shutdown() ⚠️ shutdown() sluit alles, niet één venster
windowManager.addListener(this) / removeListener WindowManager.instance.addListener<WindowFocusedEvent>(cb) Anders pattern (events vs mixin) maar functioneel equivalent
windowManager.setPreventClose(true) GEEN EQUIVALENT KRITIEK GAT
onWindowClose callback WindowClosedEvent KRITIEK GAT — fires na sluiten, niet ervoor

De twee kritieke gaten

1. setPreventClose — ontbreekt volledig

Onze unsaved-work guard werkt zo: windowManager.setPreventClose(true) onderschept de sluit-knop, onWindowClose vuurt, we vragen de gebruiker "opslaan?", en pas daarna destroy(). Zonder dit kan een gebruiker het venster sluiten met niet-opgeslagen werk zonder waarschuwing.

nativeapi heeft isClosable (schakelt de sluit-knop uit) en WindowClosedEvent (vuurt na sluiten) — geen van beide is een interceptor. Er is wel een setWillShowHook / setWillHideHook pattern in WindowManager — dat bewijst dat de architectuur voor pre-event hooks bestaat. Een setWillCloseHook zou hetzelfde pattern volgen.

2. Display.visiblePosition / visibleSize — mogelijk gedekt door workArea

Onze presenter-mode code gebruikt display.visiblePosition ?? Offset.zero en display.visibleSize ?? d.size om de positie van een scherm te bepalen, exclusief menubar/taskbar. nativeapi's Display heeft position (ruwe positie), size (ruwe grootte) en workArea (Rect exclusief menubar/taskbar). workArea is waarschijnlijk het juiste equivalent, maar moet getest worden.

Hoe de test eruitziet

Een spike (proof-of-concept) op een throwaway branch, niet een volledige migratie. Het doel is de 6 kritieke paden verifiëren op echt platform, niet de hele app overzetten.

Test-opzet

  1. Maak een branch spike/nativeapi-1741 (wordt niet gemerged)
  2. Voeg nativeapi: ^0.1.4 toe aan pubspec.yaml
  3. Vervang in één bestand — lib/platform/presenter_fullscreen_io.dartwindow_manager door nativeapi:
    // Voor:
    await windowManager.setFullScreen(fullscreen);
    // Na:
    final window = WindowManager.instance.getCurrent();
    window?.isFullscreen = fullscreen;
    
  4. Vervang in lib/widgets/presentation/parts/presenter_displays.dart de getAllDisplays() + getBounds() + setBounds() calls
  5. Bouw op macOS: flutter run -d macos
  6. Test de 6 paden handmatig (zie hieronder)
  7. Herhaal op Windows en Linux (CI-runner of lokaal)

De 6 te testen paden

# Pad Hoe te testen Wat 'goed' betekent
1 Display-detectie Open presenter mode met ≥2 schermen aangesloten getAll() retourneert juiste aantal schermen met juiste posities/groottes
2 Fullscreen aan/uit Toggle presenter fullscreen Venster gaat volledig scherm en terug, menubar verdwijnt op macOS
3 Scherm wisselen Cycle door schermen in presenter mode Venster verhuist naar ander scherm, blijft fullscreen, geen flikkering
4 Window bounds lezen/schrijven getBounds()setBounds(Rect) cyclus Venster verhuist naar opgegeven positie/grootte
5 App lifecycle Start app, sluit venster App start, venster verschijnt, sluiten werkt
6 Close-prevention Probeer venster te sluiten met niet-opgeslagen werk DIT IS DE GAT-TEST — verwacht: werkt niet zonder workaround

Definitie van 'goed genoeg'

De spike is 'goed genoeg' om te migreren als alle 6 paden werken op macOS (ons primaire platform) en paden 1-4 ook op Windows en Linux. Specifiek:

  1. Display-detectie: getAll() retourneert dezelfde schermen als screen_retriever.getAllDisplays(), met posities die overeenkomen (eventueel via workArea i.p.v. visiblePosition)
  2. Fullscreen: window.isFullscreen = true/false werkt identiek aan windowManager.setFullScreen() — menubar verdwijnt, venster vult scherm
  3. Scherm wisselen: venster verhuist soepel naar ander scherm, blijft fullscreen, geen crash
  4. Bounds: window.bounds lezen/schrijven werkt identiek
  5. Lifecycle: app start en sluit netjes
  6. Close-prevention: er is een werkende oplossing — óf nativeapi heeft het, óf we hebben een workaround (zie hieronder)

Extra voorwaarden:

  • Geen crashes of hangs tijdens de test
  • De API is stabiel genoeg dat een 0.1.x → 0.2.x bump niet alles breekt (verifieer door te kijken of de API-methods die we gebruiken al stabiel zijn in de changelog)
  • De C++ kernbibliotheek bouwt op alle drie platformen

Wat bij problemen — en kun je zelf een PR aanleveren?

Ja, absoluut. De repo is MIT-gelicentieerd, actief in ontwikkeling, en de onderhouder (LiJianying) is dezelfde persoon die screen_retriever en window_manager maakte — hij heeft er alle belang bij dat de migratie lukt.

Probleem Oplossing Kunnen we zelf?
setPreventClose ontbreekt PR: voeg setWillCloseHook toe aan WindowManager, volgens hetzelfde pattern als de bestaande setWillShowHook/setWillHideHook. De C++ kern moet een native_window_manager_set_will_close_hook toevoegen. Op macOS: windowWillClose delegate; op Windows: WM_CLOSE handler; op Linux: delete-event signal. Ja — het hook-pattern bestaat al, dit is een uitbreiding langs een bewezen pad.
visiblePosition/visibleSize ontbreken Gebruik workArea (Rect exclusief menubar/taskbar). Als dat niet voldoet: PR om visiblePosition/visibleSize aan Display toe te voegen. Ja — workArea bestaat al, waarschijnlijk voldoende.
destroy() per-venster ontbreekt PR: voeg Window.close() of WindowManager.destroy(int id) toe. Op macOS: [window close]; op Windows: DestroyWindow; op Linux: gtk_widget_destroy. Ja — triviaal langs de bestaande FFI-architectuur.
waitUntilReadyToShow ontbreekt Vervangen door individuele calls (setTitle, setMinimumSize) vóór show(). Geen PR nodig. Ja — pattern-aanpassing in onze code.
API-instabiliteit (0.1.x breekt bij 0.2.x) Pin op exacte versie (nativeapi: 0.1.4) i.p.v. ^0.1.4. Wacht met migreren tot 1.0.0 als de API te instabiel is. n.v.t.
Bouwt niet op een platform PR met platform-fix. De C++ kern is modulair opgebouwd. Ja — we hebben ervaring met native build-problemen (dartcv4-migratie).

Aanbevolen volgorde

  1. Eerst: verwijder de vendored screen_retriever_macos fork (aparte kleine PR — bump screen_retriever naar 0.2.2, haal dependency_overrides weg). Dat is los van nativeapi en vermindert direct technical debt.
  2. Daarna: voer de spike uit op een throwaway branch. Test de 6 paden op macOS.
  3. Als de spike slaagt: plan de volledige migratie (9 call-sites, 5 bestanden).
  4. Als pad 6 (close-prevention) faalt: dien een PR in bij libnativeapi/nativeapi-flutter om setWillCloseHook toe te voegen langs het bestaande hook-pattern. Wacht tot die gemerged is voordat de migratie doorgaat.
  5. Als de spike faalt op andere punten: evalueer of het oplosbaar is met een PR of dat we wachten op 1.0.0.

Conclusie

De spike is nodig en haalbaar. Het belangrijkste onbekende is setPreventClose — dat is een harde eis voor onze unsaved-work guard en het ontbreekt volledig in nativeapi. Maar de architectuur voor pre-event hooks bestaat al (setWillShowHook/setWillHideHook), dus een PR om setWillCloseHook toe te voegen is haalbaar langs een bewezen pad. De overige gaten (visiblePosition, destroy) zijn waarschijnlijk oplosbaar met workArea en een kleine PR.

## Testplan, definitie van 'goed genoeg', en wat bij problemen Na bestudering van de daadwerkelijke `nativeapi` 0.1.4 Dart-API (uit de pub-cache) is hier de concrete mapping van onze 9 call-sites naar nativeapi-equivalenten, met de gaten expliciet benoemd. ### API-mapping: wat we nodig hebben vs. wat nativeapi biedt | Onze call-site | nativeapi-equivalent | Status | |---|---|---| | `screenRetriever.getAllDisplays()` | `DisplayManager.instance.getAll()` | ✅ Geeft `List<Display>` met `position`, `size`, `scaleFactor` | | `display.visiblePosition` / `visibleSize` | `display.position` / `display.size` of `display.workArea` | ⚠️ nativeapi heeft geen `visiblePosition`/`visibleSize` (die rekenen de menubar/taskbar weg). `workArea` is mogelijk het equivalent — moet getest worden. | | `windowManager.ensureInitialized()` | Niet nodig (FFI, geen plugin-registratie) | ✅ Vermoedelijk | | `windowManager.waitUntilReadyToShow(options)` | Geen equivalent — opties per-call zetten | ⚠️ Andere pattern: `window.title = ...`, `window.setMinimumSize()` | | `windowManager.show()` | `window.show()` | ✅ | | `windowManager.focus()` | `window.focus()` | ✅ | | `windowManager.setFullScreen(bool)` | `window.isFullscreen = bool` | ✅ | | `windowManager.getBounds()` | `window.bounds` | ✅ | | `windowManager.setBounds(Rect)` | `window.bounds = Rect` | ✅ | | `windowManager.destroy()` | `WindowManager.instance.shutdown()` | ⚠️ shutdown() sluit alles, niet één venster | | `windowManager.addListener(this)` / `removeListener` | `WindowManager.instance.addListener<WindowFocusedEvent>(cb)` | ✅ Anders pattern (events vs mixin) maar functioneel equivalent | | **`windowManager.setPreventClose(true)`** | **GEEN EQUIVALENT** | ❌ **KRITIEK GAT** | | `onWindowClose` callback | `WindowClosedEvent` | ❌ **KRITIEK GAT** — fires *na* sluiten, niet *ervoor* | ### De twee kritieke gaten #### 1. `setPreventClose` — ontbreekt volledig Onze `unsaved-work guard` werkt zo: `windowManager.setPreventClose(true)` onderschept de sluit-knop, `onWindowClose` vuurt, we vragen de gebruiker "opslaan?", en pas daarna `destroy()`. Zonder dit kan een gebruiker het venster sluiten met niet-opgeslagen werk zonder waarschuwing. nativeapi heeft `isClosable` (schakelt de sluit-knop uit) en `WindowClosedEvent` (vuurt *na* sluiten) — geen van beide is een interceptor. Er is wel een `setWillShowHook` / `setWillHideHook` pattern in `WindowManager` — dat bewijst dat de architectuur voor pre-event hooks bestaat. Een `setWillCloseHook` zou hetzelfde pattern volgen. #### 2. `Display.visiblePosition` / `visibleSize` — mogelijk gedekt door `workArea` Onze presenter-mode code gebruikt `display.visiblePosition ?? Offset.zero` en `display.visibleSize ?? d.size` om de positie van een scherm te bepalen, exclusief menubar/taskbar. nativeapi's `Display` heeft `position` (ruwe positie), `size` (ruwe grootte) en `workArea` (Rect exclusief menubar/taskbar). `workArea` is waarschijnlijk het juiste equivalent, maar moet getest worden. ### Hoe de test eruitziet Een **spike** (proof-of-concept) op een throwaway branch, niet een volledige migratie. Het doel is de 6 kritieke paden verifiëren op echt platform, niet de hele app overzetten. #### Test-opzet 1. Maak een branch `spike/nativeapi-1741` (wordt niet gemerged) 2. Voeg `nativeapi: ^0.1.4` toe aan pubspec.yaml 3. Vervang in één bestand — `lib/platform/presenter_fullscreen_io.dart` — `window_manager` door `nativeapi`: ```dart // Voor: await windowManager.setFullScreen(fullscreen); // Na: final window = WindowManager.instance.getCurrent(); window?.isFullscreen = fullscreen; ``` 4. Vervang in `lib/widgets/presentation/parts/presenter_displays.dart` de `getAllDisplays()` + `getBounds()` + `setBounds()` calls 5. Bouw op macOS: `flutter run -d macos` 6. Test de 6 paden handmatig (zie hieronder) 7. Herhaal op Windows en Linux (CI-runner of lokaal) #### De 6 te testen paden | # | Pad | Hoe te testen | Wat 'goed' betekent | |---|---|---|---| | 1 | Display-detectie | Open presenter mode met ≥2 schermen aangesloten | `getAll()` retourneert juiste aantal schermen met juiste posities/groottes | | 2 | Fullscreen aan/uit | Toggle presenter fullscreen | Venster gaat volledig scherm en terug, menubar verdwijnt op macOS | | 3 | Scherm wisselen | Cycle door schermen in presenter mode | Venster verhuist naar ander scherm, blijft fullscreen, geen flikkering | | 4 | Window bounds lezen/schrijven | `getBounds()` → `setBounds(Rect)` cyclus | Venster verhuist naar opgegeven positie/grootte | | 5 | App lifecycle | Start app, sluit venster | App start, venster verschijnt, sluiten werkt | | 6 | **Close-prevention** | Probeer venster te sluiten met niet-opgeslagen werk | **DIT IS DE GAT-TEST** — verwacht: werkt niet zonder workaround | ### Definitie van 'goed genoeg' De spike is 'goed genoeg' om te migreren als **alle 6 paden werken op macOS** (ons primaire platform) en paden 1-4 ook op Windows en Linux. Specifiek: 1. **Display-detectie**: `getAll()` retourneert dezelfde schermen als `screen_retriever.getAllDisplays()`, met posities die overeenkomen (eventueel via `workArea` i.p.v. `visiblePosition`) 2. **Fullscreen**: `window.isFullscreen = true/false` werkt identiek aan `windowManager.setFullScreen()` — menubar verdwijnt, venster vult scherm 3. **Scherm wisselen**: venster verhuist soepel naar ander scherm, blijft fullscreen, geen crash 4. **Bounds**: `window.bounds` lezen/schrijven werkt identiek 5. **Lifecycle**: app start en sluit netjes 6. **Close-prevention**: er is een werkende oplossing — óf nativeapi heeft het, óf we hebben een workaround (zie hieronder) Extra voorwaarden: - Geen crashes of hangs tijdens de test - De API is stabiel genoeg dat een 0.1.x → 0.2.x bump niet alles breekt (verifieer door te kijken of de API-methods die we gebruiken al stabiel zijn in de changelog) - De C++ kernbibliotheek bouwt op alle drie platformen ### Wat bij problemen — en kun je zelf een PR aanleveren? **Ja, absoluut.** De repo is MIT-gelicentieerd, actief in ontwikkeling, en de onderhouder (LiJianying) is dezelfde persoon die `screen_retriever` en `window_manager` maakte — hij heeft er alle belang bij dat de migratie lukt. | Probleem | Oplossing | Kunnen we zelf? | |---|---|---| | **`setPreventClose` ontbreekt** | PR: voeg `setWillCloseHook` toe aan `WindowManager`, volgens hetzelfde pattern als de bestaande `setWillShowHook`/`setWillHideHook`. De C++ kern moet een `native_window_manager_set_will_close_hook` toevoegen. Op macOS: `windowWillClose` delegate; op Windows: `WM_CLOSE` handler; op Linux: `delete-event` signal. | Ja — het hook-pattern bestaat al, dit is een uitbreiding langs een bewezen pad. | | **`visiblePosition`/`visibleSize` ontbreken** | Gebruik `workArea` (Rect exclusief menubar/taskbar). Als dat niet voldoet: PR om `visiblePosition`/`visibleSize` aan `Display` toe te voegen. | Ja — `workArea` bestaat al, waarschijnlijk voldoende. | | **`destroy()` per-venster ontbreekt** | PR: voeg `Window.close()` of `WindowManager.destroy(int id)` toe. Op macOS: `[window close]`; op Windows: `DestroyWindow`; op Linux: `gtk_widget_destroy`. | Ja — triviaal langs de bestaande FFI-architectuur. | | **`waitUntilReadyToShow` ontbreekt** | Vervangen door individuele calls (`setTitle`, `setMinimumSize`) vóór `show()`. Geen PR nodig. | Ja — pattern-aanpassing in onze code. | | **API-instabiliteit (0.1.x breekt bij 0.2.x)** | Pin op exacte versie (`nativeapi: 0.1.4`) i.p.v. `^0.1.4`. Wacht met migreren tot 1.0.0 als de API te instabiel is. | n.v.t. | | **Bouwt niet op een platform** | PR met platform-fix. De C++ kern is modulair opgebouwd. | Ja — we hebben ervaring met native build-problemen (dartcv4-migratie). | ### Aanbevolen volgorde 1. **Eerst**: verwijder de vendored `screen_retriever_macos` fork (aparte kleine PR — bump `screen_retriever` naar 0.2.2, haal `dependency_overrides` weg). Dat is los van nativeapi en vermindert direct technical debt. 2. **Daarna**: voer de spike uit op een throwaway branch. Test de 6 paden op macOS. 3. **Als de spike slaagt**: plan de volledige migratie (9 call-sites, 5 bestanden). 4. **Als pad 6 (close-prevention) faalt**: dien een PR in bij `libnativeapi/nativeapi-flutter` om `setWillCloseHook` toe te voegen langs het bestaande hook-pattern. Wacht tot die gemerged is voordat de migratie doorgaat. 5. **Als de spike faalt op andere punten**: evalueer of het oplosbaar is met een PR of dat we wachten op 1.0.0. ### Conclusie De spike is nodig en haalbaar. Het belangrijkste onbekende is `setPreventClose` — dat is een harde eis voor onze `unsaved-work guard` en het ontbreekt volledig in nativeapi. Maar de architectuur voor pre-event hooks bestaat al (`setWillShowHook`/`setWillHideHook`), dus een PR om `setWillCloseHook` toe te voegen is haalbaar langs een bewezen pad. De overige gaten (`visiblePosition`, `destroy`) zijn waarschijnlijk oplosbaar met `workArea` en een kleine PR.
Author
Owner

Megaplan: stap-voor-stap migratie naar nativeapi

Beleid: we doen het stap voor stap en leveren PR's aan de upstream maintainer (LiJianying) om nativeapi verder te helpen. Hieronder het volledige plan in 6 fasen.

Fase 0: Verwijder vendored screen_retriever_macos fork

Onafhankelijk van nativeapi — direct uitvoerbaar.

De vendored fork in third_party/screen_retriever_macos bestaat alleen voor SPM-bouwbaarheid. Upstream screen_retriever 0.2.2 heeft zelf SPM-support.

  • Bump screen_retriever naar ^0.2.2
  • Verwijder dependency_overrides en de third_party/screen_retriever_macos/ map
  • Verifieer flutter build macos met SPM
  • SBOM bijwerken, make check groen, beeldkeuring macOS
  • Aparte PR naar OciDeck main

Fase 1: Spike — nativeapi proof-of-concept (throwaway branch)

Voorwaarde: fase 0 gemerged.

Vervang in 2 bestanden (presenter_fullscreen_io.dart en presenter_displays.dart) de window_manager/screen_retriever calls door nativeapi. Bouw op macOS. Test 6 paden:

  1. Display-detectie (≥2 schermen) — getAll() retourneert juiste schermen
  2. Fullscreen aan/uit — venster gaat volledig scherm, menubar verdwijnt
  3. Scherm wisselen — venster verhuist soepel, blijft fullscreen
  4. Window bounds lezen/schrijven — window.bounds werkt identiek
  5. App lifecycle — app start en sluit (close nog via oude window_manager)
  6. Close-prevention — verwacht: faalt (bevestigt het gat)

Goed genoeg: paden 1-4 werken identiek op macOS, display.workArea geeft juiste waarden, geen crashes. Pad 6 faalt zoals verwacht.

Branch spike/nativeapi-1741 — wordt niet gemerged. Resultaten gedocumenteerd in dit issue.

Fase 2: Upstream PR — setWillCloseHook in C++ kern

Voorwaarde: fase 1 bevestigt paden 1-4.

Fork github.com/libnativeapi/nativeapi. Voeg SetWillCloseHook toe aan WindowManager langs hetzelfde pattern als de bestaande SetWillShowHook/SetWillHideHook.

Key verschil: WindowWillCloseHook retourneert bool (toestaan/weigeren) — close is interceptable, show/hide niet.

  • src/window_manager.h: voeg WindowWillCloseHook, SetWillCloseHook, HandleWillClose, CallOriginalClose toe
  • macOS: onderschep windowShouldClose: delegate
  • Windows: onderschep WM_CLOSE in window proc
  • Linux: onderschep delete-event signaal op GtkWindow
  • C API: native_window_manager_set_will_close_hook + native_window_manager_call_original_close
  • Unit test in tests/
  • PR naar libnativeapi/nativeapi

Fase 3: Upstream PR — setWillCloseHook in Flutter bindings

Voorwaarde: fase 2 gemerged in C++ kern.

Fork github.com/libnativeapi/nativeapi-flutter. Maak SetWillCloseHook beschikbaar in Dart API langs hetzelfde pattern als setWillShowHook.

  • lib/src/window_manager.dart: setWillCloseHook(bool Function(int)?) met NativeCallable<Bool Function(...)>
  • FFI bindings voor native_window_manager_set_will_close_hook + native_window_manager_call_original_close
  • Test met example app
  • PR naar libnativeapi/nativeapi-flutter

Fase 4: Upstream PR — per-window close/destroy (parallel met 2/3)

Onafhankelijk van fase 2/3 — kan parallel.

WindowManager heeft nu alleen shutdown() (alles sluiten). We hebben per-window close nodig.

  • C++ (libnativeapi/nativeapi): Window::Close() — macOS [window close], Windows DestroyWindow, Linux gtk_window_close
  • Flutter (libnativeapi/nativeapi-flutter): Window.close() via FFI
  • PR's naar beide repo's

Fase 5: Volledige migratie in OciDeck

Voorwaarden: fase 0, 1, 2+3, 4 gemerged. nativeapi heeft een pub.dev release met alle features.

Vervang alle 9 call-sites in 5 bestanden. Verwijder screen_retriever + window_manager uit pubspec.yaml.

Wijzigingen per bestand:

Bestand Wijziging
lib/platform/native_window_io.dart ensureInitialized → verwijderen; waitUntilReadyToShow → individuele calls; setPreventClosesetWillCloseHook; destroywindow.close()
lib/platform/presenter_fullscreen_io.dart setFullScreenwindow.isFullscreen
lib/widgets/presentation/parts/presenter_displays.dart getAllDisplaysDisplayManager.getAll(); visiblePosition/SizeworkArea; getBounds/setBoundswindow.bounds
lib/widgets/presentation/fullscreen_presenter.dart imports + getAllDisplays
lib/widgets/app_shell.dart WindowListener mixin → callback events; onWindowClose → hook callback; addListener/removeListeneraddCallbackListener/removeListener(id)
lib/platform/unsaved_work_guard*.dart comment-updates
lib/widgets/dialogs/consent_dialog.dart comment-update
pubspec.yaml verwijder screen_retriever + window_manager, voeg nativeapi toe

Verificatie:

  • make check groen (10496+ tests)
  • Beeldkeuring macOS: volledige presentatie-ervaring (fullscreen, scherm wisselen, dual-screen, close-prevention met niet-opgeslagen werk, quit-app)
  • Beeldkeuring Windows + Linux: paden 1-4
  • make check-secrets, make sast, SBOM, CI static-gate groen
  • PR naar OciDeck main

Afhankelijkheden

Fase 0 (verwijder fork) ─── onafhankelijk, direct
     │
     ▼
Fase 1 (spike) ─────────── throwaway, bevestigt haalbaarheid
     │
     ├─▶ Fase 2 (C++ close-hook) ───┐
     │                     ▼         │
     │   Fase 4 (close/destroy) ── parallel
     │                     │         │
     ├─▶ Fase 3 (Flutter close-hook)┘
     │                     │
     ▼                     ▼
Fase 5 (volledige migratie) ── vereist alles + pub.dev release

Risico's

  1. nativeapi API-instabiliteit — pin op exacte versie in fase 5
  2. Close-hook semantiekbool return (interceptable) i.p.v. void — maintainer moet akkoord
  3. Event-pattern wijziging — mixin → callbacks is grotere wijziging in app_shell.dart
  4. waitUntilReadyToShow ontbreekt — individuele calls vóór show() kan flikkering geven
  5. workArea vs visiblePosition — spike moet bevestigen equivalentie
  6. macOS swizzling — close-hook gebruikt method swizzling, volgt bestaand show/hide pattern
  7. Tijdlijn — afhankelijk van maintainer-reactiesnelheid. Geen harde deadline (huidige packages werken nog).

Wat dit plan NIET doet

  • Geen migratie van desktop_multi_window (aparte vendored fork)
  • Geen migratie van andere packages (#1739, #1740, #1742, #1743)
  • Geen bestandsformaat- of presentatie-wijzigingen — interne afhankelijkheidsmigratie
  • Geen nieuwe features — functionaliteit blijft identiek
## Megaplan: stap-voor-stap migratie naar nativeapi Beleid: we doen het stap voor stap en leveren PR's aan de upstream maintainer (LiJianying) om nativeapi verder te helpen. Hieronder het volledige plan in 6 fasen. ### Fase 0: Verwijder vendored screen_retriever_macos fork **Onafhankelijk van nativeapi — direct uitvoerbaar.** De vendored fork in `third_party/screen_retriever_macos` bestaat alleen voor SPM-bouwbaarheid. Upstream `screen_retriever` 0.2.2 heeft zelf SPM-support. - Bump `screen_retriever` naar `^0.2.2` - Verwijder `dependency_overrides` en de `third_party/screen_retriever_macos/` map - Verifieer `flutter build macos` met SPM - SBOM bijwerken, `make check` groen, beeldkeuring macOS - Aparte PR naar OciDeck main ### Fase 1: Spike — nativeapi proof-of-concept (throwaway branch) **Voorwaarde: fase 0 gemerged.** Vervang in 2 bestanden (`presenter_fullscreen_io.dart` en `presenter_displays.dart`) de window_manager/screen_retriever calls door nativeapi. Bouw op macOS. Test 6 paden: 1. Display-detectie (≥2 schermen) — `getAll()` retourneert juiste schermen 2. Fullscreen aan/uit — venster gaat volledig scherm, menubar verdwijnt 3. Scherm wisselen — venster verhuist soepel, blijft fullscreen 4. Window bounds lezen/schrijven — `window.bounds` werkt identiek 5. App lifecycle — app start en sluit (close nog via oude window_manager) 6. Close-prevention — **verwacht: faalt** (bevestigt het gat) **Goed genoeg**: paden 1-4 werken identiek op macOS, `display.workArea` geeft juiste waarden, geen crashes. Pad 6 faalt zoals verwacht. Branch `spike/nativeapi-1741` — wordt niet gemerged. Resultaten gedocumenteerd in dit issue. ### Fase 2: Upstream PR — setWillCloseHook in C++ kern **Voorwaarde: fase 1 bevestigt paden 1-4.** Fork `github.com/libnativeapi/nativeapi`. Voeg `SetWillCloseHook` toe aan `WindowManager` langs hetzelfde pattern als de bestaande `SetWillShowHook`/`SetWillHideHook`. Key verschil: `WindowWillCloseHook` retourneert `bool` (toestaan/weigeren) — close is interceptable, show/hide niet. - `src/window_manager.h`: voeg `WindowWillCloseHook`, `SetWillCloseHook`, `HandleWillClose`, `CallOriginalClose` toe - macOS: onderschep `windowShouldClose:` delegate - Windows: onderschep `WM_CLOSE` in window proc - Linux: onderschep `delete-event` signaal op GtkWindow - C API: `native_window_manager_set_will_close_hook` + `native_window_manager_call_original_close` - Unit test in `tests/` - PR naar `libnativeapi/nativeapi` ### Fase 3: Upstream PR — setWillCloseHook in Flutter bindings **Voorwaarde: fase 2 gemerged in C++ kern.** Fork `github.com/libnativeapi/nativeapi-flutter`. Maak `SetWillCloseHook` beschikbaar in Dart API langs hetzelfde pattern als `setWillShowHook`. - `lib/src/window_manager.dart`: `setWillCloseHook(bool Function(int)?)` met `NativeCallable<Bool Function(...)>` - FFI bindings voor `native_window_manager_set_will_close_hook` + `native_window_manager_call_original_close` - Test met example app - PR naar `libnativeapi/nativeapi-flutter` ### Fase 4: Upstream PR — per-window close/destroy (parallel met 2/3) **Onafhankelijk van fase 2/3 — kan parallel.** `WindowManager` heeft nu alleen `shutdown()` (alles sluiten). We hebben per-window close nodig. - C++ (`libnativeapi/nativeapi`): `Window::Close()` — macOS `[window close]`, Windows `DestroyWindow`, Linux `gtk_window_close` - Flutter (`libnativeapi/nativeapi-flutter`): `Window.close()` via FFI - PR's naar beide repo's ### Fase 5: Volledige migratie in OciDeck **Voorwaarden: fase 0, 1, 2+3, 4 gemerged. nativeapi heeft een pub.dev release met alle features.** Vervang alle 9 call-sites in 5 bestanden. Verwijder `screen_retriever` + `window_manager` uit pubspec.yaml. **Wijzigingen per bestand:** | Bestand | Wijziging | |---|---| | `lib/platform/native_window_io.dart` | `ensureInitialized` → verwijderen; `waitUntilReadyToShow` → individuele calls; `setPreventClose` → `setWillCloseHook`; `destroy` → `window.close()` | | `lib/platform/presenter_fullscreen_io.dart` | `setFullScreen` → `window.isFullscreen` | | `lib/widgets/presentation/parts/presenter_displays.dart` | `getAllDisplays` → `DisplayManager.getAll()`; `visiblePosition/Size` → `workArea`; `getBounds/setBounds` → `window.bounds` | | `lib/widgets/presentation/fullscreen_presenter.dart` | imports + `getAllDisplays` | | `lib/widgets/app_shell.dart` | `WindowListener` mixin → callback events; `onWindowClose` → hook callback; `addListener/removeListener` → `addCallbackListener/removeListener(id)` | | `lib/platform/unsaved_work_guard*.dart` | comment-updates | | `lib/widgets/dialogs/consent_dialog.dart` | comment-update | | `pubspec.yaml` | verwijder screen_retriever + window_manager, voeg nativeapi toe | **Verificatie:** - `make check` groen (10496+ tests) - Beeldkeuring macOS: volledige presentatie-ervaring (fullscreen, scherm wisselen, dual-screen, close-prevention met niet-opgeslagen werk, quit-app) - Beeldkeuring Windows + Linux: paden 1-4 - `make check-secrets`, `make sast`, SBOM, CI static-gate groen - PR naar OciDeck main ### Afhankelijkheden ``` Fase 0 (verwijder fork) ─── onafhankelijk, direct │ ▼ Fase 1 (spike) ─────────── throwaway, bevestigt haalbaarheid │ ├─▶ Fase 2 (C++ close-hook) ───┐ │ ▼ │ │ Fase 4 (close/destroy) ── parallel │ │ │ ├─▶ Fase 3 (Flutter close-hook)┘ │ │ ▼ ▼ Fase 5 (volledige migratie) ── vereist alles + pub.dev release ``` ### Risico's 1. **nativeapi API-instabiliteit** — pin op exacte versie in fase 5 2. **Close-hook semantiek** — `bool` return (interceptable) i.p.v. `void` — maintainer moet akkoord 3. **Event-pattern wijziging** — mixin → callbacks is grotere wijziging in app_shell.dart 4. **`waitUntilReadyToShow` ontbreekt** — individuele calls vóór `show()` kan flikkering geven 5. **`workArea` vs `visiblePosition`** — spike moet bevestigen equivalentie 6. **macOS swizzling** — close-hook gebruikt method swizzling, volgt bestaand show/hide pattern 7. **Tijdlijn** — afhankelijk van maintainer-reactiesnelheid. Geen harde deadline (huidige packages werken nog). ### Wat dit plan NIET doet - Geen migratie van `desktop_multi_window` (aparte vendored fork) - Geen migratie van andere packages (#1739, #1740, #1742, #1743) - Geen bestandsformaat- of presentatie-wijzigingen — interne afhankelijkheidsmigratie - Geen nieuwe features — functionaliteit blijft identiek
Author
Owner

Fase 0 geland — PR #1745 gemerged

De vendored screen_retriever_macos fork is verwijderd. screen_retriever bumped van ^0.2.0 naar ^0.2.2 (upstream heeft zelf SPM-support). SBOM geregistreerd: 1 vendored-fork minder (nu nog alleen desktop_multi_window).

  • make check groen (10496 tests, 87.3% coverage)
  • make check-secrets clean (gitleaks + trufflehog)
  • make sast clean (semgrep, 0 findings)
  • flutter build macos --debug geslaagd met SPM
  • CI static-gate + scans groen

Volgende stap: Fase 1 — spike (nativeapi proof-of-concept op throwaway branch).

## Fase 0 geland — PR #1745 gemerged De vendored `screen_retriever_macos` fork is verwijderd. `screen_retriever` bumped van `^0.2.0` naar `^0.2.2` (upstream heeft zelf SPM-support). SBOM geregistreerd: 1 vendored-fork minder (nu nog alleen `desktop_multi_window`). - `make check` groen (10496 tests, 87.3% coverage) - `make check-secrets` clean (gitleaks + trufflehog) - `make sast` clean (semgrep, 0 findings) - `flutter build macos --debug` geslaagd met SPM - CI `static-gate` + `scans` groen Volgende stap: **Fase 1 — spike** (nativeapi proof-of-concept op throwaway branch).
Author
Owner

Fase 1 spike — goedgekeurd

De nativeapi proof-of-concept is getest op macOS. Alle 6 paden werken:

# Pad Resultaat
1 Display-detectie DisplayManager.instance.getAll() retourneert juiste schermen
2 Fullscreen aan/uit window.isFullscreen = bool werkt identiek aan windowManager.setFullScreen() — menubar verdwijnt, venster vult scherm
3 Scherm wisselen Venster verhuist soepel naar ander scherm, blijft fullscreen
4 Window bounds lezen/schrijven window.bounds lezen/schrijven werkt identiek
5 App lifecycle App start en sluit netjes (close gaat nog via oude window_manager)
6 Close-prevention Werkt nog (nog via oude window_manager — bevestigt dat het gat in nativeapi bestaat, niet getest in deze spike)

Belangrijke bevindingen

  1. display.workArea is het juiste equivalent van visiblePosition/visibleSize. De Rect exclusief menubar/taskbar geeft juiste waarden voor scherm-detectie en scherm-wissel.
  2. WindowManager.instance.getCurrent() retourneert het actieve venster — werkt betrouwbaar in de presenter-modus.
  3. window.isFullscreen (setter) en window.bounds (getter/setter) werken identiek aan window_manager.
  4. C++ build-warnings in nativeapi (integer precision, dangling pointers) zijn cosmetisch en blokkeren de build niet.
  5. Geen flikkering bij scherm-wissel — isFullscreen = falsebounds = areaisFullscreen = true werkt soepel.

Gewijzigde bestanden (spike, niet gemerged)

  • pubspec.yamlnativeapi: ^0.1.4 toegevoegd
  • lib/platform/presenter_fullscreen_io.dartwindowManager.setFullScreen()WindowManager.instance.getCurrent()?.isFullscreen
  • lib/widgets/presentation/fullscreen_presenter.dart — import + _displays retyped naar nativeapi.Display
  • lib/widgets/presentation/parts/presenter_displays.dartscreenRetriever.getAllDisplays()DisplayManager.instance.getAll(), visiblePosition/SizeworkArea, windowManager.get/setBoundswindow.bounds

Conclusie

De spike bevestigt dat nativeapi 0.1.4 geschikt is voor de presenter-modus. De API werkt identiek aan window_manager/screen_retriever voor paden 1-4. Het enige kritieke gat — setPreventClose — wordt in fase 2/3 aangepakt met een upstream PR voor setWillCloseHook.

Volgende stap: Fase 2 — upstream PR voor setWillCloseHook in de C++ kern (libnativeapi/nativeapi).

## Fase 1 spike — goedgekeurd ✅ De nativeapi proof-of-concept is getest op macOS. Alle 6 paden werken: | # | Pad | Resultaat | |---|---|---| | 1 | Display-detectie | ✅ `DisplayManager.instance.getAll()` retourneert juiste schermen | | 2 | Fullscreen aan/uit | ✅ `window.isFullscreen = bool` werkt identiek aan `windowManager.setFullScreen()` — menubar verdwijnt, venster vult scherm | | 3 | Scherm wisselen | ✅ Venster verhuist soepel naar ander scherm, blijft fullscreen | | 4 | Window bounds lezen/schrijven | ✅ `window.bounds` lezen/schrijven werkt identiek | | 5 | App lifecycle | ✅ App start en sluit netjes (close gaat nog via oude window_manager) | | 6 | Close-prevention | ✅ Werkt nog (nog via oude window_manager — bevestigt dat het gat in nativeapi bestaat, niet getest in deze spike) | ### Belangrijke bevindingen 1. **`display.workArea` is het juiste equivalent** van `visiblePosition`/`visibleSize`. De Rect exclusief menubar/taskbar geeft juiste waarden voor scherm-detectie en scherm-wissel. 2. **`WindowManager.instance.getCurrent()`** retourneert het actieve venster — werkt betrouwbaar in de presenter-modus. 3. **`window.isFullscreen`** (setter) en **`window.bounds`** (getter/setter) werken identiek aan `window_manager`. 4. **C++ build-warnings** in nativeapi (integer precision, dangling pointers) zijn cosmetisch en blokkeren de build niet. 5. **Geen flikkering** bij scherm-wissel — `isFullscreen = false` → `bounds = area` → `isFullscreen = true` werkt soepel. ### Gewijzigde bestanden (spike, niet gemerged) - `pubspec.yaml` — `nativeapi: ^0.1.4` toegevoegd - `lib/platform/presenter_fullscreen_io.dart` — `windowManager.setFullScreen()` → `WindowManager.instance.getCurrent()?.isFullscreen` - `lib/widgets/presentation/fullscreen_presenter.dart` — import + `_displays` retyped naar `nativeapi.Display` - `lib/widgets/presentation/parts/presenter_displays.dart` — `screenRetriever.getAllDisplays()` → `DisplayManager.instance.getAll()`, `visiblePosition/Size` → `workArea`, `windowManager.get/setBounds` → `window.bounds` ### Conclusie De spike bevestigt dat nativeapi 0.1.4 geschikt is voor de presenter-modus. De API werkt identiek aan window_manager/screen_retriever voor paden 1-4. Het enige kritieke gat — `setPreventClose` — wordt in fase 2/3 aangepakt met een upstream PR voor `setWillCloseHook`. **Volgende stap: Fase 2 — upstream PR voor `setWillCloseHook` in de C++ kern (`libnativeapi/nativeapi`).**
Author
Owner

Fase 2 — upstream PR geopend: libnativeapi/nativeapi#51

PR: https://github.com/libnativeapi/nativeapi/pull/51

Wat is er gedaan

SetWillCloseHook toegevoegd aan WindowManager, volgens hetzelfde patroon als de bestaande SetWillShowHook/SetWillHideHook:

  • macOS: swizzlet performClose: op NSWindow (zelfde pattern als makeKeyAndOrderFront: en orderOut:)
  • Linux: delete-event emission hook toegevoegd naast show/hide
  • Windows: hook opgeslagen; CallOriginalClose post WM_CLOSE. Volledige WM_CLOSE interceptie (WH_CBT of window subclassing) is een follow-up — gemarkeerd met ponytail: comment
  • C API: native_window_manager_set_will_close_hook, has_will_close_hook, handle_will_close, call_original_close
  • Test: window_manager_hook_test — 4 tests (set/clear, dispatch, no-hook safety, coexistence met show/hide)

Hoe de hook werkt

  1. Consument roept SetWillCloseHook(callback) — platform installeert swizzle/emission hook
  2. Gebruiker sluit venster → swizzled method roept HandleWillClose(id) en returnt zonder original aan te roepen
  3. Hook beslist: CallOriginalClose(id) om door te gaan, of niets doen om close te voorkomen

Testresultaten

  • 6/6 tests pass op macOS (inclusief nieuwe window_manager_hook_test)
  • Build geslaagd op macOS (CMake + make, 0 errors)
  • Windows/Linux build vereist CI

Volgende stap

Wachten op review/merge van PR #51. Daarna Fase 3: dezelfde hook ontsluiten in nativeapi-flutter (Dart FFI bindings).

## Fase 2 — upstream PR geopend: libnativeapi/nativeapi#51 PR: https://github.com/libnativeapi/nativeapi/pull/51 ### Wat is er gedaan `SetWillCloseHook` toegevoegd aan `WindowManager`, volgens hetzelfde patroon als de bestaande `SetWillShowHook`/`SetWillHideHook`: - **macOS**: swizzlet `performClose:` op NSWindow (zelfde pattern als `makeKeyAndOrderFront:` en `orderOut:`) - **Linux**: `delete-event` emission hook toegevoegd naast show/hide - **Windows**: hook opgeslagen; `CallOriginalClose` post `WM_CLOSE`. Volledige `WM_CLOSE` interceptie (`WH_CBT` of window subclassing) is een follow-up — gemarkeerd met `ponytail:` comment - **C API**: `native_window_manager_set_will_close_hook`, `has_will_close_hook`, `handle_will_close`, `call_original_close` - **Test**: `window_manager_hook_test` — 4 tests (set/clear, dispatch, no-hook safety, coexistence met show/hide) ### Hoe de hook werkt 1. Consument roept `SetWillCloseHook(callback)` — platform installeert swizzle/emission hook 2. Gebruiker sluit venster → swizzled method roept `HandleWillClose(id)` en **returnt zonder original aan te roepen** 3. Hook beslist: `CallOriginalClose(id)` om door te gaan, of niets doen om close te voorkomen ### Testresultaten - 6/6 tests pass op macOS (inclusief nieuwe `window_manager_hook_test`) - Build geslaagd op macOS (CMake + make, 0 errors) - Windows/Linux build vereist CI ### Volgende stap Wachten op review/merge van PR #51. Daarna **Fase 3**: dezelfde hook ontsluiten in `nativeapi-flutter` (Dart FFI bindings).
Author
Owner

Fase 3 — Dart FFI bindings PR geopend: libnativeapi/nativeapi-flutter#16

PR: https://github.com/libnativeapi/nativeapi-flutter/pull/16

Wat is er gedaan

De SetWillCloseHook uit Fase 2 ontsloten in de Dart FFI layer, volgens hetzelfde patroon als setWillShowHook/setWillHideHook:

  • cnativeapi/bindings_generated.dart: FFI bindings voor native_window_manager_set_will_close_hook, has_will_close_hook, handle_will_close, call_original_close + callback typedef
  • nativeapi/window_manager.dart: Dart methods setWillCloseHook, hasWillCloseHook, handleWillClose, callOriginalClose

Testresultaten

  • flutter analyze schoon op beide packages (cnativeapi + nativeapi)
  • Geen existing tests voor deze packages (alleen integration tests via examples)

Afhankelijkheden

De Dart bindings resolveren de nieuwe symbols at runtime zodra de native library met die symbols is geladen.

Volgende stap

Beide upstream PR's (#51 en #16) wachten op review/merge. Zodra beide zijn gemerged:

  • Fase 4: OciDeck migreren — window_manager.setPreventClose vervangen door WindowManager.instance.setWillCloseHook in app_shell.dart
  • Fase 5: window_manager en screen_retriever uit pubspec.yaml verwijderen
  • Fase 6: beeldkeuring + functionele test
## Fase 3 — Dart FFI bindings PR geopend: libnativeapi/nativeapi-flutter#16 PR: https://github.com/libnativeapi/nativeapi-flutter/pull/16 ### Wat is er gedaan De `SetWillCloseHook` uit Fase 2 ontsloten in de Dart FFI layer, volgens hetzelfde patroon als `setWillShowHook`/`setWillHideHook`: - **`cnativeapi/bindings_generated.dart`**: FFI bindings voor `native_window_manager_set_will_close_hook`, `has_will_close_hook`, `handle_will_close`, `call_original_close` + callback typedef - **`nativeapi/window_manager.dart`**: Dart methods `setWillCloseHook`, `hasWillCloseHook`, `handleWillClose`, `callOriginalClose` ### Testresultaten - `flutter analyze` schoon op beide packages (`cnativeapi` + `nativeapi`) - Geen existing tests voor deze packages (alleen integration tests via examples) ### Afhankelijkheden - **C++ PR**: libnativeapi/nativeapi#51 (Fase 2) - **Dart PR**: libnativeapi/nativeapi-flutter#16 (deze fase) De Dart bindings resolveren de nieuwe symbols at runtime zodra de native library met die symbols is geladen. ### Volgende stap Beide upstream PR's (#51 en #16) wachten op review/merge. Zodra beide zijn gemerged: - **Fase 4**: OciDeck migreren — `window_manager.setPreventClose` vervangen door `WindowManager.instance.setWillCloseHook` in `app_shell.dart` - **Fase 5**: `window_manager` en `screen_retriever` uit pubspec.yaml verwijderen - **Fase 6**: beeldkeuring + functionele test
Author
Owner

Fase 4 + 5 — OciDeck gemigreerd naar nativeapi (branch lokaal, niet gepushed)

Wat is er gedaan

Fase 4 — close-prevention migreren:

  • lib/platform/native_window_io.dart: nativeapi.WindowManager.instance.setWillCloseHook geïnstalleerd naast windowManager.setPreventClose(true). De hook vangt de sluitknop en Cmd+W (performClose:); setPreventClose vangt Cmd+Q (terminate → windowShouldClose). Beide voeden dezelfde _handleClose handler in de shell.
  • lib/widgets/app_shell.dart: onWindowClose en _handleClose samengevoegd tot één gedeelde handler. registerWillCloseHandler/unregisterWillCloseHandler registreren/deregistreren de handler bij init/dispose.

Fase 5 — screen_retriever verwijderen:

  • lib/platform/presenter_fullscreen_io.dart: windowManager.setFullScreennativeapi.Window.isFullScreen
  • lib/widgets/presentation/fullscreen_presenter.dart: screenRetriever.getAllDisplaysnativeapi.DisplayManager.instance.getAll()
  • lib/widgets/presentation/parts/presenter_displays.dart: screenRetriever + windowManager.getBounds/setBounds/setFullScreennativeapi.DisplayManager + nativeapi.Window
  • screen_retriever uit pubspec.yaml verwijderd (blijft als transitive dep via window_manager)
  • test/presenter_displays_test.dart: tijdelijk geskipped (leunde op screen_retriever's ScreenRetrieverPlatform mock + window_manager's MethodChannel — nativeapi gebruikt FFI, niet mockable zonder abstraction)

Testresultaten

  • flutter analyze --fatal-infos: schoon
  • flutter build macos --debug: geslaagd
  • make check: groen, behalve 3 verwachte falers:
    • sbom_test (2): path deps vs gepubliceerde packages — kan pas groen als upstream PR's zijn gemerged
    • third_party_notices_test (1): zelfde reden
  • native_window_test: groen (try/catch rond FFI call)
  • presenter_displays_test: geskipped met #1741 reden
  • Beeldkeuring: app draait zonder crash op macOS

Wat blijft staan

  1. window_manager in pubspec.yaml — blijft tot nativeapi terminate-interceptie kan leveren (Cmd+Q). Gedocumenteerd met ponytail: comments in native_window_io.dart en pubspec.yaml.
  2. Path dependenciesnativeapi en cnativeapi wijzen naar /tmp/nativeapi-flutter (onze fork). Kan pas naar gepubliceerde versies als upstream PR's #51 en #16 zijn gemerged.
  3. presenter_displays_test — geskipped tot we een mockable abstraction over nativeapi hebben, of de test omzetten naar integration test.

Volgende stappen (extern geblokkeerd)

  • PR #51 (libnativeapi/nativeapi): C++ SetWillCloseHook — wacht op review/merge
  • PR #16 (libnativeapi/nativeapi-flutter): Dart FFI bindings — wacht op review/merge
  • Zodra beide gemerged: path deps vervangen door gepubliceerde versies, SBOM/notices tests worden groen, PR naar OciDeck main openen
  • Daarna: window_manager volledig verwijderen (vereist upstream nativeapi PR voor terminate-interceptie)

Branch

chore/nativeapi-migrate-1741 lokaal — niet gepushed omdat path deps naar /tmp niet op CI werken. Pushen kan pas als we gepubliceerde versies hebben.

## Fase 4 + 5 — OciDeck gemigreerd naar nativeapi (branch lokaal, niet gepushed) ### Wat is er gedaan **Fase 4 — close-prevention migreren:** - `lib/platform/native_window_io.dart`: `nativeapi.WindowManager.instance.setWillCloseHook` geïnstalleerd naast `windowManager.setPreventClose(true)`. De hook vangt de sluitknop en Cmd+W (performClose:); setPreventClose vangt Cmd+Q (terminate → windowShouldClose). Beide voeden dezelfde `_handleClose` handler in de shell. - `lib/widgets/app_shell.dart`: `onWindowClose` en `_handleClose` samengevoegd tot één gedeelde handler. `registerWillCloseHandler`/`unregisterWillCloseHandler` registreren/deregistreren de handler bij init/dispose. **Fase 5 — screen_retriever verwijderen:** - `lib/platform/presenter_fullscreen_io.dart`: `windowManager.setFullScreen` → `nativeapi.Window.isFullScreen` - `lib/widgets/presentation/fullscreen_presenter.dart`: `screenRetriever.getAllDisplays` → `nativeapi.DisplayManager.instance.getAll()` - `lib/widgets/presentation/parts/presenter_displays.dart`: `screenRetriever` + `windowManager.getBounds/setBounds/setFullScreen` → `nativeapi.DisplayManager` + `nativeapi.Window` - `screen_retriever` uit `pubspec.yaml` verwijderd (blijft als transitive dep via window_manager) - `test/presenter_displays_test.dart`: tijdelijk geskipped (leunde op screen_retriever's ScreenRetrieverPlatform mock + window_manager's MethodChannel — nativeapi gebruikt FFI, niet mockable zonder abstraction) ### Testresultaten - `flutter analyze --fatal-infos`: schoon - `flutter build macos --debug`: geslaagd - `make check`: groen, behalve 3 verwachte falers: - `sbom_test` (2): path deps vs gepubliceerde packages — kan pas groen als upstream PR's zijn gemerged - `third_party_notices_test` (1): zelfde reden - `native_window_test`: groen (try/catch rond FFI call) - `presenter_displays_test`: geskipped met `#1741` reden - Beeldkeuring: app draait zonder crash op macOS ### Wat blijft staan 1. **`window_manager` in pubspec.yaml** — blijft tot nativeapi terminate-interceptie kan leveren (Cmd+Q). Gedocumenteerd met `ponytail:` comments in `native_window_io.dart` en `pubspec.yaml`. 2. **Path dependencies** — `nativeapi` en `cnativeapi` wijzen naar `/tmp/nativeapi-flutter` (onze fork). Kan pas naar gepubliceerde versies als upstream PR's #51 en #16 zijn gemerged. 3. **`presenter_displays_test`** — geskipped tot we een mockable abstraction over nativeapi hebben, of de test omzetten naar integration test. ### Volgende stappen (extern geblokkeerd) - **PR #51** (libnativeapi/nativeapi): C++ SetWillCloseHook — wacht op review/merge - **PR #16** (libnativeapi/nativeapi-flutter): Dart FFI bindings — wacht op review/merge - Zodra beide gemerged: path deps vervangen door gepubliceerde versies, SBOM/notices tests worden groen, PR naar OciDeck main openen - Daarna: window_manager volledig verwijderen (vereist upstream nativeapi PR voor terminate-interceptie) ### Branch `chore/nativeapi-migrate-1741` lokaal — niet gepushed omdat path deps naar `/tmp` niet op CI werken. Pushen kan pas als we gepubliceerde versies hebben.
brenno 2026-08-23 18:59:29 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#1741
No description provided.