feat: refine presenter and language options
This commit is contained in:
parent
d0bd1a85bf
commit
d59c6ee761
9 changed files with 752 additions and 222 deletions
|
|
@ -14,11 +14,12 @@ class OciDeckApp extends ConsumerWidget {
|
|||
final languageCode = ref.watch(
|
||||
settingsProvider.select((s) => s.languageCode),
|
||||
);
|
||||
AppLocalizations.setActiveLanguageCode(languageCode);
|
||||
return MaterialApp(
|
||||
title: 'OciDeck',
|
||||
theme: AppTheme.light,
|
||||
debugShowCheckedModeBanner: false,
|
||||
locale: Locale(languageCode),
|
||||
locale: AppLocalizations.materialLocaleFor(languageCode),
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ class AppLocalizations {
|
|||
Locale('de'),
|
||||
Locale('fr'),
|
||||
Locale('es'),
|
||||
Locale('fy'),
|
||||
Locale('pap'),
|
||||
];
|
||||
|
||||
static const languageNames = {
|
||||
|
|
@ -22,8 +24,31 @@ class AppLocalizations {
|
|||
'de': 'Deutsch',
|
||||
'fr': 'Français',
|
||||
'es': 'Español',
|
||||
'fy': 'Frysk',
|
||||
'pap': 'Papiamento',
|
||||
};
|
||||
|
||||
static const _materialLocaleFallbacks = {
|
||||
'nl': Locale('nl'),
|
||||
'en': Locale('en'),
|
||||
'it': Locale('it'),
|
||||
'de': Locale('de'),
|
||||
'fr': Locale('fr'),
|
||||
'es': Locale('es'),
|
||||
'fy': Locale('en'),
|
||||
'pap': Locale('en'),
|
||||
};
|
||||
|
||||
static String _activeLanguageCode = 'nl';
|
||||
|
||||
static void setActiveLanguageCode(String code) {
|
||||
_activeLanguageCode = languageNames.containsKey(code) ? code : 'nl';
|
||||
}
|
||||
|
||||
static Locale materialLocaleFor(String code) {
|
||||
return _materialLocaleFallbacks[code] ?? const Locale('nl');
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||
_AppLocalizationsDelegate();
|
||||
|
||||
|
|
@ -32,13 +57,19 @@ class AppLocalizations {
|
|||
const AppLocalizations(Locale('nl'));
|
||||
}
|
||||
|
||||
String get languageCode => _activeLanguageCode;
|
||||
|
||||
String t(String key) {
|
||||
return _strings[locale.languageCode]?[key] ?? _strings['nl']![key] ?? key;
|
||||
if (languageCode == 'nl') return _strings['nl']![key] ?? key;
|
||||
return _strings[languageCode]?[key] ??
|
||||
_strings['en']?[key] ??
|
||||
_strings['nl']![key] ??
|
||||
key;
|
||||
}
|
||||
|
||||
String d(String dutchText) {
|
||||
if (locale.languageCode == 'nl') return dutchText;
|
||||
return _dutchSourceStrings[locale.languageCode]?[dutchText] ??
|
||||
if (languageCode == 'nl') return dutchText;
|
||||
return _dutchSourceStrings[languageCode]?[dutchText] ??
|
||||
_dutchSourceStrings['en']?[dutchText] ??
|
||||
dutchText;
|
||||
}
|
||||
|
|
@ -567,6 +598,168 @@ const _strings = {
|
|||
'of': 'de',
|
||||
'exportedTo': 'Exportado a:',
|
||||
},
|
||||
'fy': {
|
||||
'newPresentation': 'Nije presintaasje',
|
||||
'open': 'Iepenje...',
|
||||
'openEllipsis': 'Iepenje…',
|
||||
'recentPresentations': 'Resinte presintaasjes',
|
||||
'newTab': 'Nij ljepblêd',
|
||||
'undo': 'Ungedien meitsje (Ctrl/Cmd+Z)',
|
||||
'redo': 'Opnij útfiere (Ctrl/Cmd+Shift+Z)',
|
||||
'imageLibrary': 'Ofbyldingsbibleteek',
|
||||
'presentFullscreen': 'Presintearje folslein skerm · P foar presenter view',
|
||||
'visualMode': 'Fisuele modus',
|
||||
'markdownMode': 'Markdown-modus',
|
||||
'save': 'Bewarje',
|
||||
'saveShortcut': 'Bewarje (Ctrl/Cmd+S)',
|
||||
'more': 'Mear',
|
||||
'export': 'Eksportearje',
|
||||
'exportReady': 'Eksportearje (PDF/PPTX/HTML)',
|
||||
'exportNeedsSave': 'Bewarje de presintaasje earst om te eksportearjen',
|
||||
'exportNeedsClean': 'Bewarje dyn wizigingen earst om te eksportearjen',
|
||||
'saved': 'Bewarre',
|
||||
'unsaved': 'Net bewarre',
|
||||
'unsavedChanges': 'Wizigingen bewarje (Ctrl/Cmd+S)',
|
||||
'noUnsavedChanges': 'Gjin net-bewarre wizigingen',
|
||||
'notSavedYet': 'Noch net bewarre',
|
||||
'noFileYet': 'Dizze presintaasje hat noch gjin bestân',
|
||||
'slides': 'slides',
|
||||
'skipped': 'oerslein',
|
||||
'allSlidesIncluded': 'Alle slides wurde presintearre en eksportearre',
|
||||
'skippedSlidesExcluded': 'slide(s) wurde net presintearre of eksportearre',
|
||||
'styleProfile': 'Stylprofyl',
|
||||
'classification': 'Klassifikaasje',
|
||||
'exportNextToDeck': 'Eksport neist deck',
|
||||
'exportsNextToDeck': 'Eksporten wurde neist it deck bewarre',
|
||||
'exportFolder': 'Eksport',
|
||||
'newPresentationTab': 'Nije presintaasje (ljepblêd)',
|
||||
'exportPackage': 'Pakket eksportearje…',
|
||||
'importPackage': 'Pakket ymportearje…',
|
||||
'importUrl': 'Ymportearje fan URL…',
|
||||
'findReplace': 'Sykje en ferfange',
|
||||
'fullDeckPreview': 'Hiel deck besjen',
|
||||
'presentationProperties': 'Presintaasje-eigenskippen',
|
||||
'settings': 'Ynstellingen',
|
||||
'settingsGeneral': 'Algemien',
|
||||
'settingsColors': 'Kleuren',
|
||||
'settingsLogo': 'Logo',
|
||||
'language': 'Taal',
|
||||
'applicationLanguage': 'Applikaasjetaal',
|
||||
'languageHelp':
|
||||
'De interface wikselet daliks fan taal. Presintaasje-ynhâld bliuwt itselde.',
|
||||
'presentationFolder': 'Presintaasjemap',
|
||||
'exportFolderSetting': 'Eksportmap',
|
||||
'notSet': 'Net ynsteld',
|
||||
'nextToPresentationFile': 'Neist it presintaasjebestân',
|
||||
'choose': 'Kieze',
|
||||
'removeDefaultFolder': 'Standertmap fuortsmite',
|
||||
'removeExportFolder': 'Eksportmap fuortsmite',
|
||||
'exportFolderHelp':
|
||||
'Alle eksporten (PDF/PPTX) wurde hjir bewarre. Net ynsteld? Dan komme se neist it presintaasjebestân.',
|
||||
'cancel': 'Annulearje',
|
||||
'close': 'Slute',
|
||||
'saveSettings': 'Bewarje',
|
||||
'exportDialogTitle': 'Eksportearje',
|
||||
'exportAgain': 'Nochris eksportearje',
|
||||
'exportIntro':
|
||||
'De eksport brûkt krekt de werjefte út de editor, ynklusyf dyn stylprofyl.',
|
||||
'imageQualityPdf': 'Ofbyldingskwaliteit (PDF)',
|
||||
'normal': 'Normaal',
|
||||
'compressed': 'Komprimearre',
|
||||
'compressedHelp':
|
||||
'JPEG op legere resolúsje, bedoeld as handout, mei in folle lytser bestân (apart bewarre as “-compact”).',
|
||||
'losslessHelp': 'Ferliesfrije ôfbyldings op folsleine resolúsje.',
|
||||
'exportAsPdf': 'Eksportearje as PDF',
|
||||
'exportAsPptx': 'Eksportearje as PPTX',
|
||||
'exportAsHtml': 'Eksportearje as HTML (Marp, offline)',
|
||||
'renderingSlides': 'Slides renderje…',
|
||||
'buildingHtml': 'HTML bouwe…',
|
||||
'buildingExport': 'bouwe…',
|
||||
'slideOf': 'Slide',
|
||||
'of': 'fan',
|
||||
'exportedTo': 'Eksportearre nei:',
|
||||
},
|
||||
'pap': {
|
||||
'newPresentation': 'Presentashon nobo',
|
||||
'open': 'Habri...',
|
||||
'openEllipsis': 'Habri…',
|
||||
'recentPresentations': 'Presentashonnan resien',
|
||||
'newTab': 'Tab nobo',
|
||||
'undo': 'Deshasí (Ctrl/Cmd+Z)',
|
||||
'redo': 'Hasi atrobe (Ctrl/Cmd+Shift+Z)',
|
||||
'imageLibrary': 'Biblioteka di imágen',
|
||||
'presentFullscreen': 'Presentá na pantalla kompletu · P pa presenter view',
|
||||
'visualMode': 'Modo visual',
|
||||
'markdownMode': 'Modo Markdown',
|
||||
'save': 'Warda',
|
||||
'saveShortcut': 'Warda (Ctrl/Cmd+S)',
|
||||
'more': 'Mas',
|
||||
'export': 'Eksportá',
|
||||
'exportReady': 'Eksportá (PDF/PPTX/HTML)',
|
||||
'exportNeedsSave': 'Warda e presentashon promé ku eksportá',
|
||||
'exportNeedsClean': 'Warda bo kambionan promé ku eksportá',
|
||||
'saved': 'Wardá',
|
||||
'unsaved': 'No wardá',
|
||||
'unsavedChanges': 'Warda kambionan (Ctrl/Cmd+S)',
|
||||
'noUnsavedChanges': 'No tin kambionan sin warda',
|
||||
'notSavedYet': 'Ainda no wardá',
|
||||
'noFileYet': 'E presentashon aki ainda no tin un file',
|
||||
'slides': 'slides',
|
||||
'skipped': 'saltá',
|
||||
'allSlidesIncluded': 'Tur slides lo wordu presentá i eksportá',
|
||||
'skippedSlidesExcluded': 'slide(s) no lo wordu presentá òf eksportá',
|
||||
'styleProfile': 'Perfil di estilo',
|
||||
'classification': 'Klasifikashon',
|
||||
'exportNextToDeck': 'Eksportá banda di deck',
|
||||
'exportsNextToDeck': 'Eksportnan ta wordu wardá banda di e deck',
|
||||
'exportFolder': 'Eksport',
|
||||
'newPresentationTab': 'Presentashon nobo (tab)',
|
||||
'exportPackage': 'Eksportá pakete…',
|
||||
'importPackage': 'Importá pakete…',
|
||||
'importUrl': 'Importá for di URL…',
|
||||
'findReplace': 'Busca i reemplasá',
|
||||
'fullDeckPreview': 'Mira henter e deck',
|
||||
'presentationProperties': 'Propiedatnan di presentashon',
|
||||
'settings': 'Preferensianan',
|
||||
'settingsGeneral': 'General',
|
||||
'settingsColors': 'Kolónan',
|
||||
'settingsLogo': 'Logo',
|
||||
'language': 'Idioma',
|
||||
'applicationLanguage': 'Idioma di aplikashon',
|
||||
'languageHelp':
|
||||
'E interface ta cambia idioma mesora. Kontenido di presentashon ta keda igual.',
|
||||
'presentationFolder': 'Folder di presentashon',
|
||||
'exportFolderSetting': 'Folder di eksport',
|
||||
'notSet': 'No konfigurá',
|
||||
'nextToPresentationFile': 'Banda di e file di presentashon',
|
||||
'choose': 'Skohe',
|
||||
'removeDefaultFolder': 'Kita folder standard',
|
||||
'removeExportFolder': 'Kita folder di eksport',
|
||||
'exportFolderHelp':
|
||||
'Tur eksportnan (PDF/PPTX) ta wordu wardá aki. Si no konfigurá, nan ta wordu wardá banda di e file di presentashon.',
|
||||
'cancel': 'Kanselá',
|
||||
'close': 'Sera',
|
||||
'saveSettings': 'Warda',
|
||||
'exportDialogTitle': 'Eksportá',
|
||||
'exportAgain': 'Eksportá atrobe',
|
||||
'exportIntro':
|
||||
'E eksport ta usa mesun bista ku e editor, inkluyendo bo perfil di estilo.',
|
||||
'imageQualityPdf': 'Kalidat di imágen (PDF)',
|
||||
'normal': 'Normal',
|
||||
'compressed': 'Komprimí',
|
||||
'compressedHelp':
|
||||
'JPEG ku resolushon mas abou, pa handout, ku un file hopi mas chikí (wardá apart komo “-compact”).',
|
||||
'losslessHelp': 'Imágennan sin pèrdida na resolushon kompletu.',
|
||||
'exportAsPdf': 'Eksportá komo PDF',
|
||||
'exportAsPptx': 'Eksportá komo PPTX',
|
||||
'exportAsHtml': 'Eksportá komo HTML (Marp, offline)',
|
||||
'renderingSlides': 'Render slides…',
|
||||
'buildingHtml': 'Trahando HTML…',
|
||||
'buildingExport': 'trahando…',
|
||||
'slideOf': 'Slide',
|
||||
'of': 'di',
|
||||
'exportedTo': 'Eksportá na:',
|
||||
},
|
||||
};
|
||||
|
||||
const _dutchSourceStrings = {
|
||||
|
|
@ -852,21 +1045,26 @@ const _dutchSourceStrings = {
|
|||
'↑↓←→ navigeren · Enter kiezen · Dubbelklik selecteert':
|
||||
'↑↓←→ navigate · Enter chooses · Double-click selects',
|
||||
'Sneltoetsen': 'Keyboard shortcuts',
|
||||
'Toetsenlegenda': 'Key legend',
|
||||
'spatie': 'space',
|
||||
'klik': 'click',
|
||||
'cijfers': 'numbers',
|
||||
'Klik of druk op ? / H / Esc om te sluiten':
|
||||
'Click or press ? / H / Esc to close',
|
||||
'Klik of druk op H / Esc om te sluiten': 'Click or press H / Esc to close',
|
||||
'Naar slidenummer': 'Go to slide number',
|
||||
'Eerste · laatste slide': 'First · last slide',
|
||||
'Slide-overzicht': 'Slide overview',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Slide overview (arrows + Enter)',
|
||||
'Presenter view (notities, klok)': 'Presenter view (notes, clock)',
|
||||
'Scherm wisselen (meerdere schermen)': 'Switch screen (multiple displays)',
|
||||
'Zwart · wit scherm': 'Black · white screen',
|
||||
'Verstreken tijd resetten': 'Reset elapsed time',
|
||||
'Automatische modus aan/uit': 'Automatic mode on/off',
|
||||
'Herhalen (loop) aan/uit': 'Repeat (loop) on/off',
|
||||
'Na audio automatisch doorgaan': 'Advance automatically after audio',
|
||||
'Dit overzicht': 'This overview',
|
||||
'Deze legenda': 'This legend',
|
||||
'Terug / afsluiten': 'Back / exit',
|
||||
'Auto (A)': 'Auto (A)',
|
||||
'Handmatig (A)': 'Manual (A)',
|
||||
|
|
@ -883,8 +1081,16 @@ const _dutchSourceStrings = {
|
|||
'Verstreken': 'Elapsed',
|
||||
'Klok': 'Clock',
|
||||
'Geen notities voor deze slide.': 'No notes for this slide.',
|
||||
'Wissel scherm (S)': 'Switch screen (S)',
|
||||
'Kon niet van scherm wisselen.': 'Could not switch screens.',
|
||||
'P publiek · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P audience · G overview · B/W black/white · R time · Esc stop',
|
||||
'P publiek · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P audience · S screen · G overview · B/W black/white · R time · Esc stop',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P audience · H legend · G overview · B/W black/white · R time · Esc stop',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P audience · H legend · S screen · G overview · B/W black/white · R time · Esc stop',
|
||||
'pijltjes + Enter of klik om te springen':
|
||||
'arrows + Enter or click to jump',
|
||||
'Afsluiten (Escape)': 'Exit (Escape)',
|
||||
|
|
@ -1033,12 +1239,35 @@ const _dutchSourceStrings = {
|
|||
'Gekopieerd': 'Copiato',
|
||||
'Afbeelding verwijderen?': 'Eliminare immagine?',
|
||||
'Sneltoetsen': 'Scorciatoie da tastiera',
|
||||
'Toetsenlegenda': 'Legenda tasti',
|
||||
'spatie': 'spazio',
|
||||
'klik': 'clic',
|
||||
'cijfers': 'numeri',
|
||||
'Klik of druk op H / Esc om te sluiten':
|
||||
'Fai clic o premi H / Esc per chiudere',
|
||||
'Naar slidenummer': 'Vai al numero slide',
|
||||
'Eerste · laatste slide': 'Prima · ultima slide',
|
||||
'Slide-overzicht': 'Panoramica slide',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Panoramica slide (frecce + Enter)',
|
||||
'Presenter view (notities, klok)': 'Vista relatore (note, orologio)',
|
||||
'Scherm wisselen (meerdere schermen)': 'Cambia schermo (piu schermi)',
|
||||
'Zwart · wit scherm': 'Schermo nero · bianco',
|
||||
'Verstreken tijd resetten': 'Reimposta tempo trascorso',
|
||||
'Automatische modus aan/uit': 'Modalita automatica on/off',
|
||||
'Herhalen (loop) aan/uit': 'Ripetizione (loop) on/off',
|
||||
'Na audio automatisch doorgaan': 'Avanza automaticamente dopo audio',
|
||||
'Deze legenda': 'Questa legenda',
|
||||
'Terug / afsluiten': 'Indietro / esci',
|
||||
'HUIDIGE SLIDE': 'SLIDE ATTUALE',
|
||||
'VOLGENDE': 'PROSSIMA',
|
||||
'NOTITIES': 'NOTE',
|
||||
'Verstreken': 'Trascorso',
|
||||
'Klok': 'Orologio',
|
||||
'Wissel scherm (S)': 'Cambia schermo (S)',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P pubblico · H legenda · G panoramica · B/W nero/bianco · R tempo · Esc stop',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P pubblico · H legenda · S schermo · G panoramica · B/W nero/bianco · R tempo · Esc stop',
|
||||
},
|
||||
'de': {
|
||||
'Geen': 'Keine',
|
||||
|
|
@ -1158,12 +1387,36 @@ const _dutchSourceStrings = {
|
|||
'Gekopieerd': 'Kopiert',
|
||||
'Afbeelding verwijderen?': 'Bild löschen?',
|
||||
'Sneltoetsen': 'Tastenkürzel',
|
||||
'Toetsenlegenda': 'Tastenlegende',
|
||||
'spatie': 'Leertaste',
|
||||
'klik': 'Klick',
|
||||
'cijfers': 'Zahlen',
|
||||
'Klik of druk op H / Esc om te sluiten':
|
||||
'Klicken oder H / Esc drücken zum Schließen',
|
||||
'Naar slidenummer': 'Zu Foliennummer',
|
||||
'Eerste · laatste slide': 'Erste · letzte Folie',
|
||||
'Slide-overzicht': 'Folienübersicht',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Folienübersicht (Pfeile + Enter)',
|
||||
'Presenter view (notities, klok)': 'Presenter-Ansicht (Notizen, Uhr)',
|
||||
'Scherm wisselen (meerdere schermen)':
|
||||
'Bildschirm wechseln (mehrere Bildschirme)',
|
||||
'Zwart · wit scherm': 'Schwarzer · weißer Bildschirm',
|
||||
'Verstreken tijd resetten': 'Vergangene Zeit zurücksetzen',
|
||||
'Automatische modus aan/uit': 'Automatikmodus ein/aus',
|
||||
'Herhalen (loop) aan/uit': 'Wiederholen (Loop) ein/aus',
|
||||
'Na audio automatisch doorgaan': 'Nach Audio automatisch weiter',
|
||||
'Deze legenda': 'Diese Legende',
|
||||
'Terug / afsluiten': 'Zurück / beenden',
|
||||
'HUIDIGE SLIDE': 'AKTUELLE FOLIE',
|
||||
'VOLGENDE': 'NÄCHSTE',
|
||||
'NOTITIES': 'NOTIZEN',
|
||||
'Verstreken': 'Vergangen',
|
||||
'Klok': 'Uhr',
|
||||
'Wissel scherm (S)': 'Bildschirm wechseln (S)',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P Publikum · H Legende · G Übersicht · B/W schwarz/weiß · R Zeit · Esc Stopp',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P Publikum · H Legende · S Bildschirm · G Übersicht · B/W schwarz/weiß · R Zeit · Esc Stopp',
|
||||
},
|
||||
'fr': {
|
||||
'Geen': 'Aucun',
|
||||
|
|
@ -1283,12 +1536,35 @@ const _dutchSourceStrings = {
|
|||
'Gekopieerd': 'Copié',
|
||||
'Afbeelding verwijderen?': 'Supprimer l’image ?',
|
||||
'Sneltoetsen': 'Raccourcis clavier',
|
||||
'Toetsenlegenda': 'Legende des touches',
|
||||
'spatie': 'espace',
|
||||
'klik': 'clic',
|
||||
'cijfers': 'chiffres',
|
||||
'Klik of druk op H / Esc om te sluiten':
|
||||
'Cliquez ou appuyez sur H / Esc pour fermer',
|
||||
'Naar slidenummer': 'Aller au numero de diapositive',
|
||||
'Eerste · laatste slide': 'Premiere · derniere diapositive',
|
||||
'Slide-overzicht': 'Vue d’ensemble',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Vue d’ensemble (fleches + Entree)',
|
||||
'Presenter view (notities, klok)': 'Vue presentateur (notes, horloge)',
|
||||
'Scherm wisselen (meerdere schermen)': 'Changer d’ecran (plusieurs ecrans)',
|
||||
'Zwart · wit scherm': 'Ecran noir · blanc',
|
||||
'Verstreken tijd resetten': 'Reinitialiser le temps ecoule',
|
||||
'Automatische modus aan/uit': 'Mode automatique active/desactive',
|
||||
'Herhalen (loop) aan/uit': 'Boucle activee/desactivee',
|
||||
'Na audio automatisch doorgaan': 'Avancer automatiquement apres l’audio',
|
||||
'Deze legenda': 'Cette legende',
|
||||
'Terug / afsluiten': 'Retour / quitter',
|
||||
'HUIDIGE SLIDE': 'DIAPOSITIVE ACTUELLE',
|
||||
'VOLGENDE': 'SUIVANTE',
|
||||
'NOTITIES': 'NOTES',
|
||||
'Verstreken': 'Écoulé',
|
||||
'Klok': 'Horloge',
|
||||
'Wissel scherm (S)': 'Changer d’ecran (S)',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P public · H legende · G vue d’ensemble · B/W noir/blanc · R temps · Esc arret',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P public · H legende · S ecran · G vue d’ensemble · B/W noir/blanc · R temps · Esc arret',
|
||||
},
|
||||
'es': {
|
||||
'Geen': 'Ninguno',
|
||||
|
|
@ -1408,11 +1684,334 @@ const _dutchSourceStrings = {
|
|||
'Gekopieerd': 'Copiado',
|
||||
'Afbeelding verwijderen?': '¿Eliminar imagen?',
|
||||
'Sneltoetsen': 'Atajos de teclado',
|
||||
'Toetsenlegenda': 'Leyenda de teclas',
|
||||
'spatie': 'espacio',
|
||||
'klik': 'clic',
|
||||
'cijfers': 'números',
|
||||
'Klik of druk op H / Esc om te sluiten':
|
||||
'Haz clic o pulsa H / Esc para cerrar',
|
||||
'Naar slidenummer': 'Ir al número de diapositiva',
|
||||
'Eerste · laatste slide': 'Primera · última diapositiva',
|
||||
'Slide-overzicht': 'Vista general',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Vista general (flechas + Enter)',
|
||||
'Presenter view (notities, klok)': 'Vista de presentador (notas, reloj)',
|
||||
'Scherm wisselen (meerdere schermen)':
|
||||
'Cambiar pantalla (varias pantallas)',
|
||||
'Zwart · wit scherm': 'Pantalla negra · blanca',
|
||||
'Verstreken tijd resetten': 'Restablecer tiempo transcurrido',
|
||||
'Automatische modus aan/uit': 'Modo automático activar/desactivar',
|
||||
'Herhalen (loop) aan/uit': 'Repetir (bucle) activar/desactivar',
|
||||
'Na audio automatisch doorgaan': 'Avanzar automáticamente tras el audio',
|
||||
'Deze legenda': 'Esta leyenda',
|
||||
'Terug / afsluiten': 'Volver / salir',
|
||||
'HUIDIGE SLIDE': 'DIAPOSITIVA ACTUAL',
|
||||
'VOLGENDE': 'SIGUIENTE',
|
||||
'NOTITIES': 'NOTAS',
|
||||
'Verstreken': 'Transcurrido',
|
||||
'Klok': 'Reloj',
|
||||
'Wissel scherm (S)': 'Cambiar pantalla (S)',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P público · H leyenda · G vista general · B/W negro/blanco · R tiempo · Esc detener',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P público · H leyenda · S pantalla · G vista general · B/W negro/blanco · R tiempo · Esc detener',
|
||||
},
|
||||
'fy': {
|
||||
'Geen': 'Gjin',
|
||||
'Nieuw': 'Nij',
|
||||
'Verwijderen': 'Fuortsmite',
|
||||
'Herstellen': 'Weromsette',
|
||||
'Opslaan en sluiten': 'Bewarje en slute',
|
||||
'Importeren via URL': 'Ymportearje fan URL',
|
||||
'Ophalen': 'Ophelje',
|
||||
'Titelpagina': 'Titelslide',
|
||||
'Tussentitel': 'Tuskenkop',
|
||||
'Alleen Bullets': 'Allinnich bullets',
|
||||
'Twee Bulletkolommen': 'Twa bulletkolommen',
|
||||
'Bullets + Afbeelding': 'Bullets + Ofbylding',
|
||||
'Twee Afbeeldingen': 'Twa ôfbyldings',
|
||||
'Grote Afbeelding': 'Grutte ôfbylding',
|
||||
'Tabel': 'Tabel',
|
||||
'Vrije Markdown': 'Frije Markdown',
|
||||
'Overgeslagen': 'Oerslein',
|
||||
'Kopiëren': 'Kopiearje',
|
||||
'Kopieer als afbeelding': 'Kopiearje as ôfbylding',
|
||||
'Dupliceren': 'Duplisearje',
|
||||
'Niet meer overslaan': 'Net mear oerslaan',
|
||||
'Overslaan': 'Oerslaan',
|
||||
'Titel': 'Titel',
|
||||
'Titel (optioneel)': 'Titel (opsjoneel)',
|
||||
'Slide titel': 'Slidetitel',
|
||||
'Ondertitel': 'Undertitel',
|
||||
'Optionele subtitel': 'Opsjonele subtitel',
|
||||
'Bullets': 'Bullets',
|
||||
'Bullet toevoegen': 'Bullet tafoegje',
|
||||
'Verwijder': 'Fuortsmite',
|
||||
'Citaat': 'Sitaat',
|
||||
'Auteur': 'Auteur',
|
||||
'Achtergrondafbeelding': 'Eftergrûnôfbylding',
|
||||
'Achtergrondafbeelding (optioneel)': 'Eftergrûnôfbylding (opsjoneel)',
|
||||
'Zoom achtergrond': 'Eftergrûn zoom',
|
||||
'Zoom afbeelding': 'Ofbylding zoom',
|
||||
'Afbeelding (rechts)': 'Ofbylding (rjochts)',
|
||||
'Bullets (links)': 'Bullets (lofts)',
|
||||
'Linker afbeelding': 'Linker ôfbylding',
|
||||
'Rechter afbeelding': 'Rjochter ôfbylding',
|
||||
'Audio bij deze slide': 'Audio by dizze slide',
|
||||
'Audio automatisch afspelen': 'Audio automatysk ôfspylje',
|
||||
'Video automatisch afspelen': 'Fideo automatysk ôfspylje',
|
||||
'Geen audiobestand gekozen': 'Gjin audiobestân keazen',
|
||||
'Geen video gekozen': 'Gjin fideo keazen',
|
||||
'Kiezen': 'Kieze',
|
||||
'Uit bibliotheek…': 'Ut bibleteek…',
|
||||
'Van computer…': 'Fan kompjûter…',
|
||||
'Geen afbeelding gekozen': 'Gjin ôfbylding keazen',
|
||||
'Caption / bronvermelding': 'Byskrift / boarne',
|
||||
'Beschrijving (doorzoekbaar)': 'Beskriuwing (trochsykber)',
|
||||
'Markdown inhoud': 'Markdown-ynhâld',
|
||||
'Rij toevoegen': 'Rige tafoegje',
|
||||
'Kolom toevoegen': 'Kolom tafoegje',
|
||||
'Kolom': 'Kolom',
|
||||
'Presentatie openen': 'Presintaasje iepenje',
|
||||
'Opslaan als': 'Bewarje as',
|
||||
'Pakket importeren': 'Pakket ymportearje',
|
||||
'Pakket exporteren': 'Pakket eksportearje',
|
||||
'Bladeren…': 'Blêdzje…',
|
||||
'Geen map gekozen': 'Gjin map keazen',
|
||||
'Map kiezen': 'Map kieze',
|
||||
'Slide zoeken': 'Slide sykje',
|
||||
'Slides importeren': 'Slides ymportearje',
|
||||
'Importeren': 'Ymportearje',
|
||||
'Klaar': 'Klear',
|
||||
'Toevoegen': 'Tafoegje',
|
||||
'Toegevoegd': 'Tafoege',
|
||||
'Selecteer alles': 'Alles selektearje',
|
||||
'Deselecteer alles': 'Alles deselektearje',
|
||||
'Zoeken en vervangen': 'Sykje en ferfange',
|
||||
'Zoeken naar': 'Sykje nei',
|
||||
'Vervangen door': 'Ferfange troch',
|
||||
'Hoofdlettergevoelig': 'Haadlettergefoelich',
|
||||
'Vervang alles': 'Alles ferfange',
|
||||
'Nieuwe presentatie': 'Nije presintaasje',
|
||||
'Aanmaken': 'Oanmeitsje',
|
||||
'Slide type kiezen': 'Slidetype kieze',
|
||||
'Presentatie-eigenschappen': 'Presintaasje-eigenskippen',
|
||||
'Versie': 'Ferzje',
|
||||
'Organisatie': 'Organisaasje',
|
||||
'Datum': 'Datum',
|
||||
'Beschrijving': 'Beskriuwing',
|
||||
'Trefwoorden': 'Trefwurden',
|
||||
'Profielnaam': 'Profylnamme',
|
||||
'Stijlprofiel': 'Stylprofyl',
|
||||
'Lettertype': 'Lettertype',
|
||||
'Kleuren': 'Kleuren',
|
||||
'Tekst': 'Tekst',
|
||||
'Logo': 'Logo',
|
||||
'Geen logo ingesteld': 'Gjin logo ynsteld',
|
||||
'Logo positie': 'Logoposysje',
|
||||
'Linksboven': 'Loftsboppe',
|
||||
'Rechtsboven': 'Rjochtsboppe',
|
||||
'Linksonder': 'Loftsûnder',
|
||||
'Rechtsonder': 'Rjochtsûnder',
|
||||
'Footertekst': 'Footer-tekst',
|
||||
'Footerpositie': 'Footer-posysje',
|
||||
'Links': 'Lofts',
|
||||
'Midden': 'Midden',
|
||||
'Rechts': 'Rjochts',
|
||||
'Voorvertoning': 'Foarbyld',
|
||||
'Preview': 'Foarbyld',
|
||||
'Uitzoomen': 'Utzoome',
|
||||
'Inzoomen': 'Ynzoome',
|
||||
'Zoom resetten': 'Zoom weromsette',
|
||||
'Vorige slide': 'Foarige slide',
|
||||
'Volgende slide': 'Folgjende slide',
|
||||
'Thema': 'Tema',
|
||||
'Afbeelding kiezen': 'Ofbylding kieze',
|
||||
'Afbeeldingen laden…': 'Ofbyldings lade…',
|
||||
'Sluiten (Esc)': 'Slute (Esc)',
|
||||
'Raster': 'Raster',
|
||||
'Geen afbeeldingen gevonden': 'Gjin ôfbyldings fûn',
|
||||
'Gekopieerd': 'Kopiearre',
|
||||
'Afbeelding verwijderen?': 'Ofbylding fuortsmite?',
|
||||
'Sneltoetsen': 'Fluchtoetsen',
|
||||
'Toetsenlegenda': 'Toetsleginda',
|
||||
'spatie': 'spaasje',
|
||||
'klik': 'klik',
|
||||
'cijfers': 'sifers',
|
||||
'Klik of druk op H / Esc om te sluiten':
|
||||
'Klik of druk op H / Esc om te sluten',
|
||||
'Naar slidenummer': 'Nei slidenûmer',
|
||||
'Eerste · laatste slide': 'Earste · lêste slide',
|
||||
'Slide-overzicht': 'Slide-oersjoch',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Slide-oersjoch (pylken + Enter)',
|
||||
'Presenter view (notities, klok)': 'Presenter view (notysjes, klok)',
|
||||
'Scherm wisselen (meerdere schermen)': 'Skerm wikselje (mear skermen)',
|
||||
'Zwart · wit scherm': 'Swart · wyt skerm',
|
||||
'Verstreken tijd resetten': 'Ferrûne tiid weromsette',
|
||||
'Automatische modus aan/uit': 'Automatyske modus oan/út',
|
||||
'Herhalen (loop) aan/uit': 'Werhelje (loop) oan/út',
|
||||
'Na audio automatisch doorgaan': 'Nei audio automatysk trochgean',
|
||||
'Deze legenda': 'Dizze leginda',
|
||||
'Terug / afsluiten': 'Werom / ôfslute',
|
||||
'HUIDIGE SLIDE': 'AKTUELE SLIDE',
|
||||
'VOLGENDE': 'FOLGJENDE',
|
||||
'NOTITIES': 'NOTYSJES',
|
||||
'Verstreken': 'Ferrûn',
|
||||
'Klok': 'Klok',
|
||||
'Geen notities voor deze slide.': 'Gjin notysjes foar dizze slide.',
|
||||
'Wissel scherm (S)': 'Skerm wikselje (S)',
|
||||
'Kon niet van scherm wisselen.': 'Koe net fan skerm wikselje.',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P publyk · H leginda · G oersjoch · B/W swart/wyt · R tiid · Esc stop',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P publyk · H leginda · S skerm · G oersjoch · B/W swart/wyt · R tiid · Esc stop',
|
||||
},
|
||||
'pap': {
|
||||
'Geen': 'Ningun',
|
||||
'Nieuw': 'Nobo',
|
||||
'Verwijderen': 'Kita',
|
||||
'Herstellen': 'Restorá',
|
||||
'Opslaan en sluiten': 'Warda i sera',
|
||||
'Importeren via URL': 'Importá for di URL',
|
||||
'Ophalen': 'Tuma',
|
||||
'Titelpagina': 'Slide di título',
|
||||
'Tussentitel': 'Título di sekshon',
|
||||
'Alleen Bullets': 'Solamente bullets',
|
||||
'Twee Bulletkolommen': 'Dos kolòm di bullets',
|
||||
'Bullets + Afbeelding': 'Bullets + Imágen',
|
||||
'Twee Afbeeldingen': 'Dos imágen',
|
||||
'Grote Afbeelding': 'Imágen grandi',
|
||||
'Tabel': 'Tabel',
|
||||
'Vrije Markdown': 'Markdown liber',
|
||||
'Overgeslagen': 'Saltá',
|
||||
'Kopiëren': 'Kopia',
|
||||
'Kopieer als afbeelding': 'Kopia komo imágen',
|
||||
'Dupliceren': 'Dupliká',
|
||||
'Niet meer overslaan': 'No salta mas',
|
||||
'Overslaan': 'Salta',
|
||||
'Titel': 'Título',
|
||||
'Titel (optioneel)': 'Título (opshonal)',
|
||||
'Slide titel': 'Título di slide',
|
||||
'Ondertitel': 'Subtítulo',
|
||||
'Optionele subtitel': 'Subtítulo opshonal',
|
||||
'Bullets': 'Bullets',
|
||||
'Bullet toevoegen': 'Añadí bullet',
|
||||
'Verwijder': 'Kita',
|
||||
'Citaat': 'Sitá',
|
||||
'Auteur': 'Outor',
|
||||
'Achtergrondafbeelding': 'Imágen di fondo',
|
||||
'Achtergrondafbeelding (optioneel)': 'Imágen di fondo (opshonal)',
|
||||
'Zoom achtergrond': 'Zoom di fondo',
|
||||
'Zoom afbeelding': 'Zoom di imágen',
|
||||
'Afbeelding (rechts)': 'Imágen (banda drechi)',
|
||||
'Bullets (links)': 'Bullets (banda robes)',
|
||||
'Linker afbeelding': 'Imágen robes',
|
||||
'Rechter afbeelding': 'Imágen drechi',
|
||||
'Audio bij deze slide': 'Audio pa e slide aki',
|
||||
'Audio automatisch afspelen': 'Toka audio outomátiko',
|
||||
'Video automatisch afspelen': 'Toka video outomátiko',
|
||||
'Geen audiobestand gekozen': 'Ningun file di audio skohe',
|
||||
'Geen video gekozen': 'Ningun video skohe',
|
||||
'Kiezen': 'Skohe',
|
||||
'Uit bibliotheek…': 'For di biblioteka…',
|
||||
'Van computer…': 'For di kòmpiuter…',
|
||||
'Geen afbeelding gekozen': 'Ningun imágen skohe',
|
||||
'Caption / bronvermelding': 'Caption / fuente',
|
||||
'Beschrijving (doorzoekbaar)': 'Deskripshon (buskabel)',
|
||||
'Markdown inhoud': 'Kontenido Markdown',
|
||||
'Rij toevoegen': 'Añadí rei',
|
||||
'Kolom toevoegen': 'Añadí kolòm',
|
||||
'Kolom': 'Kolòm',
|
||||
'Presentatie openen': 'Habri presentashon',
|
||||
'Opslaan als': 'Warda komo',
|
||||
'Pakket importeren': 'Importá pakete',
|
||||
'Pakket exporteren': 'Eksportá pakete',
|
||||
'Bladeren…': 'Navegá…',
|
||||
'Geen map gekozen': 'Ningun folder skohe',
|
||||
'Map kiezen': 'Skohe folder',
|
||||
'Slide zoeken': 'Busca slide',
|
||||
'Slides importeren': 'Importá slides',
|
||||
'Importeren': 'Importá',
|
||||
'Klaar': 'Kla',
|
||||
'Toevoegen': 'Añadí',
|
||||
'Toegevoegd': 'Añadí',
|
||||
'Selecteer alles': 'Selektá tur',
|
||||
'Deselecteer alles': 'Deselektá tur',
|
||||
'Zoeken en vervangen': 'Busca i reemplasá',
|
||||
'Zoeken naar': 'Busca',
|
||||
'Vervangen door': 'Reemplasá ku',
|
||||
'Hoofdlettergevoelig': 'Sensitivo pa mayúskula',
|
||||
'Vervang alles': 'Reemplasá tur',
|
||||
'Nieuwe presentatie': 'Presentashon nobo',
|
||||
'Aanmaken': 'Krea',
|
||||
'Slide type kiezen': 'Skohe tipo di slide',
|
||||
'Presentatie-eigenschappen': 'Propiedatnan di presentashon',
|
||||
'Versie': 'Vershon',
|
||||
'Organisatie': 'Organisashon',
|
||||
'Datum': 'Fecha',
|
||||
'Beschrijving': 'Deskripshon',
|
||||
'Trefwoorden': 'Palabranan klave',
|
||||
'Profielnaam': 'Nòmber di perfil',
|
||||
'Stijlprofiel': 'Perfil di estilo',
|
||||
'Lettertype': 'Font',
|
||||
'Kleuren': 'Kolónan',
|
||||
'Tekst': 'Teksto',
|
||||
'Logo': 'Logo',
|
||||
'Geen logo ingesteld': 'Ningun logo konfigurá',
|
||||
'Logo positie': 'Posishon di logo',
|
||||
'Linksboven': 'Ariba robes',
|
||||
'Rechtsboven': 'Ariba drechi',
|
||||
'Linksonder': 'Abou robes',
|
||||
'Rechtsonder': 'Abou drechi',
|
||||
'Footertekst': 'Teksto di footer',
|
||||
'Footerpositie': 'Posishon di footer',
|
||||
'Links': 'Robes',
|
||||
'Midden': 'Meimei',
|
||||
'Rechts': 'Drechi',
|
||||
'Voorvertoning': 'Preview',
|
||||
'Preview': 'Preview',
|
||||
'Uitzoomen': 'Zoom out',
|
||||
'Inzoomen': 'Zoom in',
|
||||
'Zoom resetten': 'Reset zoom',
|
||||
'Vorige slide': 'Slide anterior',
|
||||
'Volgende slide': 'Siguiente slide',
|
||||
'Thema': 'Tema',
|
||||
'Afbeelding kiezen': 'Skohe imágen',
|
||||
'Afbeeldingen laden…': 'Kargando imágennan…',
|
||||
'Sluiten (Esc)': 'Sera (Esc)',
|
||||
'Raster': 'Grid',
|
||||
'Geen afbeeldingen gevonden': 'No a haña imágen',
|
||||
'Gekopieerd': 'Kopiá',
|
||||
'Afbeelding verwijderen?': 'Kita imágen?',
|
||||
'Sneltoetsen': 'Atahonan di tekla',
|
||||
'Toetsenlegenda': 'Legenda di tekla',
|
||||
'spatie': 'spasio',
|
||||
'klik': 'klik',
|
||||
'cijfers': 'numbernan',
|
||||
'Klik of druk op H / Esc om te sluiten': 'Klik òf primi H / Esc pa sera',
|
||||
'Naar slidenummer': 'Bai na number di slide',
|
||||
'Eerste · laatste slide': 'Promé · último slide',
|
||||
'Slide-overzicht': 'Resumen di slides',
|
||||
'Slide-overzicht (pijltjes + Enter)': 'Resumen di slides (flecha + Enter)',
|
||||
'Presenter view (notities, klok)': 'Presenter view (notanan, oloshi)',
|
||||
'Scherm wisselen (meerdere schermen)': 'Kambia pantalla (mas pantalla)',
|
||||
'Zwart · wit scherm': 'Pantalla pretu · blanku',
|
||||
'Verstreken tijd resetten': 'Reset tempu transkurí',
|
||||
'Automatische modus aan/uit': 'Modo outomátiko on/off',
|
||||
'Herhalen (loop) aan/uit': 'Ripití (loop) on/off',
|
||||
'Na audio automatisch doorgaan': 'Sigui outomátiko despues di audio',
|
||||
'Deze legenda': 'E legenda aki',
|
||||
'Terug / afsluiten': 'Bèk / sali',
|
||||
'HUIDIGE SLIDE': 'SLIDE AKTUAL',
|
||||
'VOLGENDE': 'SIGUIENTE',
|
||||
'NOTITIES': 'NOTANAN',
|
||||
'Verstreken': 'Transkurí',
|
||||
'Klok': 'Oloshi',
|
||||
'Geen notities voor deze slide.': 'No tin nota pa e slide aki.',
|
||||
'Wissel scherm (S)': 'Kambia pantalla (S)',
|
||||
'Kon niet van scherm wisselen.': 'No por a kambia pantalla.',
|
||||
'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P públiko · H legenda · G resumen · B/W pretu/blanku · R tempu · Esc stop',
|
||||
'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop':
|
||||
'P públiko · H legenda · S pantalla · G resumen · B/W pretu/blanku · R tempu · Esc stop',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1184,13 +1184,6 @@ class _MainLayoutState extends ConsumerState<_MainLayout> {
|
|||
onPressed: saveDeck,
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: exportTooltip,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.upload_file_outlined, size: 18),
|
||||
onPressed: canExport ? exportDeck : null,
|
||||
),
|
||||
),
|
||||
const _ActionsDivider(),
|
||||
// ── Overig (minder vaak gebruikt) ───────────────────────────
|
||||
PopupMenuButton<String>(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:screen_retriever/screen_retriever.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import '../../models/deck.dart';
|
||||
import '../../models/settings.dart';
|
||||
|
|
@ -107,6 +108,12 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
/// Met M te wisselen.
|
||||
bool _advanceOnAudioEnd = true;
|
||||
|
||||
/// Known displays for moving the fullscreen presentation window. This is not
|
||||
/// a second presenter window; it keeps the current output movable between
|
||||
/// screens with S or the presenter-view button.
|
||||
List<Display> _displays = const [];
|
||||
int _displayIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -119,6 +126,7 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_focusNode.requestFocus();
|
||||
_loadDisplays();
|
||||
_scheduleAdvance();
|
||||
});
|
||||
}
|
||||
|
|
@ -209,6 +217,55 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
_scheduleAdvance();
|
||||
}
|
||||
|
||||
Future<void> _loadDisplays() async {
|
||||
try {
|
||||
final displays = await screenRetriever.getAllDisplays();
|
||||
if (!mounted || displays.isEmpty) return;
|
||||
final bounds = await windowManager.getBounds();
|
||||
final center = bounds.center;
|
||||
final current = displays.indexWhere((d) {
|
||||
final p = d.visiblePosition ?? Offset.zero;
|
||||
final s = d.visibleSize ?? d.size;
|
||||
return Rect.fromLTWH(p.dx, p.dy, s.width, s.height).contains(center);
|
||||
});
|
||||
setState(() {
|
||||
_displays = displays;
|
||||
_displayIndex = current < 0 ? 0 : current;
|
||||
});
|
||||
} catch (_) {
|
||||
// Screen detection is best-effort; presenting should still work.
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _moveToDisplay(int index) async {
|
||||
if (_displays.length < 2) return;
|
||||
final display = _displays[index.clamp(0, _displays.length - 1)];
|
||||
final position = display.visiblePosition ?? Offset.zero;
|
||||
final size = display.visibleSize ?? display.size;
|
||||
try {
|
||||
await windowManager.setFullScreen(false);
|
||||
await windowManager.setBounds(
|
||||
Rect.fromLTWH(position.dx, position.dy, size.width, size.height),
|
||||
);
|
||||
await windowManager.setFullScreen(true);
|
||||
if (mounted) setState(() => _displayIndex = index);
|
||||
} catch (_) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.d('Kon niet van scherm wisselen.')),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _cycleDisplay() async {
|
||||
if (_displays.isEmpty) await _loadDisplays();
|
||||
if (_displays.length < 2) return;
|
||||
await _moveToDisplay((_displayIndex + 1) % _displays.length);
|
||||
}
|
||||
|
||||
Future<void> _exit() async {
|
||||
_advanceTimer?.cancel();
|
||||
await windowManager.setFullScreen(false);
|
||||
|
|
@ -452,6 +509,9 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
case LogicalKeyboardKey.keyM:
|
||||
_toggleAudioAdvance();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.keyS:
|
||||
_cycleDisplay();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.escape:
|
||||
// Gelaagd: getypt nummer wissen, dan blanco scherm, dan pas afsluiten.
|
||||
if (_typed.isNotEmpty) {
|
||||
|
|
@ -587,12 +647,13 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
('Home · End', l10n.d('Eerste · laatste slide')),
|
||||
('G', l10n.d('Slide-overzicht (pijltjes + Enter)')),
|
||||
('P', l10n.d('Presenter view (notities, klok)')),
|
||||
('S', l10n.d('Scherm wisselen (meerdere schermen)')),
|
||||
('B · W', l10n.d('Zwart · wit scherm')),
|
||||
('R', l10n.d('Verstreken tijd resetten')),
|
||||
('A', l10n.d('Automatische modus aan/uit')),
|
||||
('L', l10n.d('Herhalen (loop) aan/uit')),
|
||||
('M', l10n.d('Na audio automatisch doorgaan')),
|
||||
('? · H', l10n.d('Dit overzicht')),
|
||||
('H', l10n.d('Deze legenda')),
|
||||
('Esc', l10n.d('Terug / afsluiten')),
|
||||
];
|
||||
return GestureDetector(
|
||||
|
|
@ -627,7 +688,7 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
l10n.d('Sneltoetsen'),
|
||||
l10n.d('Toetsenlegenda'),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
|
|
@ -668,7 +729,7 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: Text(
|
||||
l10n.d('Klik of druk op ? / H / Esc om te sluiten'),
|
||||
l10n.d('Klik of druk op H / Esc om te sluiten'),
|
||||
style: const TextStyle(
|
||||
color: Colors.white30,
|
||||
fontSize: 12,
|
||||
|
|
@ -684,44 +745,6 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
);
|
||||
}
|
||||
|
||||
/// Subtiele statusindicator (linksonder) voor de automatische modus. Toont
|
||||
/// of auto-play, herhalen en 'na audio doorgaan' actief zijn.
|
||||
Widget _autoPlayStatus() {
|
||||
final l10n = context.l10n;
|
||||
final items = <(IconData, String, bool)>[
|
||||
(
|
||||
_autoPlay ? Icons.play_circle_outline : Icons.pause_circle_outline,
|
||||
_autoPlay ? l10n.d('Auto (A)') : l10n.d('Handmatig (A)'),
|
||||
_autoPlay,
|
||||
),
|
||||
(Icons.repeat, l10n.d('Herhalen (L)'), _loop),
|
||||
(Icons.graphic_eq, l10n.d('Na audio (M)'), _advanceOnAudioEnd),
|
||||
];
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final (icon, label, active) in items)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: Opacity(
|
||||
opacity: active ? 0.7 : 0.28,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 14, color: Colors.white),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 11),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Vol-vlak zwart/wit scherm dat met een klik weer verdwijnt.
|
||||
Widget _blankFill() {
|
||||
return GestureDetector(
|
||||
|
|
@ -783,148 +806,7 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
return GestureDetector(
|
||||
onTap: _next,
|
||||
onSecondaryTap: _prev,
|
||||
child: Stack(
|
||||
children: [
|
||||
// ── Slide canvas ─────────────────────────────────────────────────
|
||||
Positioned.fill(child: _slideCanvas(slide)),
|
||||
|
||||
// ── Voortgangsbalk (auto-advance) ────────────────────────────────
|
||||
if (_progress > 0)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: LinearProgressIndicator(
|
||||
value: _progress,
|
||||
backgroundColor: Colors.white12,
|
||||
color: Colors.white54,
|
||||
minHeight: 3,
|
||||
),
|
||||
),
|
||||
|
||||
// ── Slide counter ────────────────────────────────────────────────
|
||||
Positioned(
|
||||
right: 24,
|
||||
bottom: 10,
|
||||
child: Text(
|
||||
'${_index + 1} / $total',
|
||||
style: const TextStyle(
|
||||
color: Colors.white38,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ── Auto-play status (linksonder) ────────────────────────────────
|
||||
Positioned(left: 24, bottom: 10, child: _autoPlayStatus()),
|
||||
|
||||
// ── Navigation arrows ────────────────────────────────────────────
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: MouseRegion(
|
||||
cursor: _index > 0 ? SystemMouseCursors.click : MouseCursor.defer,
|
||||
child: GestureDetector(
|
||||
onTap: _prev,
|
||||
child: Container(
|
||||
width: 60,
|
||||
color: Colors.transparent,
|
||||
child: _index > 0
|
||||
? const Icon(
|
||||
Icons.chevron_left,
|
||||
color: Colors.white24,
|
||||
size: 40,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: MouseRegion(
|
||||
cursor: _index < total - 1
|
||||
? SystemMouseCursors.click
|
||||
: MouseCursor.defer,
|
||||
child: GestureDetector(
|
||||
onTap: _next,
|
||||
child: Container(
|
||||
width: 60,
|
||||
color: Colors.transparent,
|
||||
child: _index < total - 1
|
||||
? const Icon(
|
||||
Icons.chevron_right,
|
||||
color: Colors.white24,
|
||||
size: 40,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ── Top-right controls (presenter view + afsluiten) ──────────────
|
||||
Positioned(
|
||||
top: 16,
|
||||
right: 16,
|
||||
child: Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: context.l10n.d('Sneltoetsen (?)'),
|
||||
child: IconButton(
|
||||
onPressed: _toggleHelp,
|
||||
icon: const Icon(Icons.help_outline),
|
||||
color: Colors.white,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.black45,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Tooltip(
|
||||
message: context.l10n.d('Slide-overzicht (G)'),
|
||||
child: IconButton(
|
||||
onPressed: _toggleGrid,
|
||||
icon: const Icon(Icons.grid_view_rounded),
|
||||
color: Colors.white,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.black45,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Tooltip(
|
||||
message: context.l10n.d('Presenter view (P)'),
|
||||
child: IconButton(
|
||||
onPressed: _togglePresenterView,
|
||||
icon: const Icon(Icons.co_present_outlined),
|
||||
color: Colors.white,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.black45,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Tooltip(
|
||||
message: context.l10n.d('Afsluiten (Escape)'),
|
||||
child: IconButton(
|
||||
onPressed: _exit,
|
||||
icon: const Icon(Icons.close),
|
||||
color: Colors.white,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.black45,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SizedBox.expand(child: _slideCanvas(slide)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1151,6 +1033,16 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
icon: Icons.chevron_right,
|
||||
onTap: _index < total - 1 ? _next : null,
|
||||
),
|
||||
if (_displays.length > 1) ...[
|
||||
const SizedBox(width: 8),
|
||||
Tooltip(
|
||||
message: l10n.d('Wissel scherm (S)'),
|
||||
child: _NavButton(
|
||||
icon: Icons.screen_share_outlined,
|
||||
onTap: _cycleDisplay,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'${l10n.d('Slide')} ${_index + 1} / $total',
|
||||
|
|
@ -1164,7 +1056,9 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
|||
Expanded(
|
||||
child: Text(
|
||||
l10n.d(
|
||||
'P publiek · G overzicht · B/W zwart/wit · R tijd · Esc stop',
|
||||
_displays.length > 1
|
||||
? 'P publiek · H legenda · S scherm · G overzicht · B/W zwart/wit · R tijd · Esc stop'
|
||||
: 'P publiek · H legenda · G overzicht · B/W zwart/wit · R tijd · Esc stop',
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
maxLines: 1,
|
||||
|
|
|
|||
|
|
@ -654,7 +654,7 @@ packages:
|
|||
source: hosted
|
||||
version: "3.2.1"
|
||||
screen_retriever:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: screen_retriever
|
||||
sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ dependencies:
|
|||
path_provider: ^2.1.5
|
||||
path: ^1.9.1
|
||||
uuid: ^4.5.1
|
||||
screen_retriever: ^0.2.0
|
||||
window_manager: ^0.5.1
|
||||
shared_preferences: ^2.3.3
|
||||
pasteboard: ^0.5.0
|
||||
|
|
|
|||
35
test/app_localizations_test.dart
Normal file
35
test/app_localizations_test.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ocideck/l10n/app_localizations.dart';
|
||||
|
||||
void main() {
|
||||
tearDown(() => AppLocalizations.setActiveLanguageCode('nl'));
|
||||
|
||||
test('supports Frisian and Papiamento language choices', () {
|
||||
expect(AppLocalizations.languageNames['fy'], 'Frysk');
|
||||
expect(AppLocalizations.languageNames['pap'], 'Papiamento');
|
||||
expect(AppLocalizations.supportedLocales, contains(const Locale('fy')));
|
||||
expect(AppLocalizations.supportedLocales, contains(const Locale('pap')));
|
||||
});
|
||||
|
||||
test('uses app translations while Material falls back safely', () {
|
||||
AppLocalizations.setActiveLanguageCode('fy');
|
||||
expect(const AppLocalizations(Locale('en')).t('settings'), 'Ynstellingen');
|
||||
expect(
|
||||
const AppLocalizations(Locale('en')).d('Toetsenlegenda'),
|
||||
'Toetsleginda',
|
||||
);
|
||||
expect(AppLocalizations.materialLocaleFor('fy'), const Locale('en'));
|
||||
|
||||
AppLocalizations.setActiveLanguageCode('pap');
|
||||
expect(
|
||||
const AppLocalizations(Locale('en')).t('settings'),
|
||||
'Preferensianan',
|
||||
);
|
||||
expect(
|
||||
const AppLocalizations(Locale('en')).d('Toetsenlegenda'),
|
||||
'Legenda di tekla',
|
||||
);
|
||||
expect(AppLocalizations.materialLocaleFor('pap'), const Locale('en'));
|
||||
});
|
||||
}
|
||||
|
|
@ -29,9 +29,9 @@ void main() {
|
|||
});
|
||||
|
||||
testWidgets('free Markdown renders display math', (tester) async {
|
||||
final slide = Slide.create(SlideType.freeMarkdown).copyWith(
|
||||
customMarkdown: 'Stelling:\n\n\$\$E = mc^2\$\$\n',
|
||||
);
|
||||
final slide = Slide.create(
|
||||
SlideType.freeMarkdown,
|
||||
).copyWith(customMarkdown: 'Stelling:\n\n\$\$E = mc^2\$\$\n');
|
||||
await tester.pumpWidget(_host(slide));
|
||||
|
||||
expect(find.byType(Math), findsOneWidget);
|
||||
|
|
@ -40,9 +40,9 @@ void main() {
|
|||
testWidgets('an unknown code language falls back without throwing', (
|
||||
tester,
|
||||
) async {
|
||||
final slide = Slide.create(SlideType.freeMarkdown).copyWith(
|
||||
customMarkdown: '```nonexistentlang\nsome code\n```\n',
|
||||
);
|
||||
final slide = Slide.create(
|
||||
SlideType.freeMarkdown,
|
||||
).copyWith(customMarkdown: '```nonexistentlang\nsome code\n```\n');
|
||||
await tester.pumpWidget(_host(slide));
|
||||
|
||||
// No HighlightView (unknown language) and no exception during build.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,12 @@ void main() {
|
|||
await tester.pumpWidget(_host(slides));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('1 / 2'), findsOneWidget); // slide counter
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
expect(find.text('1 / 2'), findsNothing); // no audience chrome
|
||||
expect(find.byIcon(Icons.help_outline), findsNothing);
|
||||
expect(find.byIcon(Icons.grid_view_rounded), findsNothing);
|
||||
expect(find.byIcon(Icons.co_present_outlined), findsNothing);
|
||||
expect(find.byIcon(Icons.close), findsNothing);
|
||||
expect(find.text('NOTITIES'), findsNothing); // presenter-only
|
||||
expect(find.text('VOLGENDE'), findsNothing);
|
||||
|
||||
|
|
@ -88,17 +93,17 @@ void main() {
|
|||
testWidgets('B blanks the audience screen and toggles back', (tester) async {
|
||||
await tester.pumpWidget(_host(slides));
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
// Blank to black: the slide (and its counter) disappears.
|
||||
// Blank to black: the slide disappears.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyB);
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsNothing);
|
||||
expect(find.text('Eerste'), findsNothing);
|
||||
|
||||
// Same key restores the slide.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyB);
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
@ -111,12 +116,12 @@ void main() {
|
|||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyB);
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsNothing);
|
||||
expect(find.text('Eerste'), findsNothing);
|
||||
|
||||
// First arrow un-blanks without advancing (still on slide 1).
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
@ -142,7 +147,7 @@ void main() {
|
|||
|
||||
// Grid closed and we jumped to slide 2.
|
||||
expect(find.text('Slide-overzicht'), findsNothing);
|
||||
expect(find.text('2 / 2'), findsOneWidget);
|
||||
expect(find.text('Tweede'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
@ -152,15 +157,15 @@ void main() {
|
|||
) async {
|
||||
await tester.pumpWidget(_host(slides));
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.end);
|
||||
await tester.pump();
|
||||
expect(find.text('2 / 2'), findsOneWidget);
|
||||
expect(find.text('Tweede'), findsOneWidget);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.home);
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
@ -187,7 +192,7 @@ void main() {
|
|||
await tester.pump();
|
||||
|
||||
expect(find.text('Slide-overzicht'), findsNothing);
|
||||
expect(find.text('2 / 2'), findsOneWidget);
|
||||
expect(find.text('Tweede'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
@ -199,7 +204,7 @@ void main() {
|
|||
];
|
||||
await tester.pumpWidget(_host(three));
|
||||
await tester.pump();
|
||||
expect(find.text('1 / 3'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
// Type "3" → a badge appears, Enter jumps to slide 3.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.digit3);
|
||||
|
|
@ -208,8 +213,9 @@ void main() {
|
|||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.pump();
|
||||
// Badge gone, now actually on slide 3 (counter shows it).
|
||||
expect(find.text('3 / 3'), findsOneWidget); // slide counter now
|
||||
// Badge gone, now actually on slide 3.
|
||||
expect(find.text('Derde'), findsOneWidget);
|
||||
expect(find.text('3 / 3'), findsNothing);
|
||||
expect(find.byIcon(Icons.south_east), findsNothing); // badge icon gone
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
|
|
@ -218,17 +224,18 @@ void main() {
|
|||
testWidgets('? toggles the shortcut cheatsheet', (tester) async {
|
||||
await tester.pumpWidget(_host(slides));
|
||||
await tester.pump();
|
||||
expect(find.text('Sneltoetsen'), findsNothing);
|
||||
expect(find.text('Toetsenlegenda'), findsNothing);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyH);
|
||||
await tester.pump();
|
||||
expect(find.text('Sneltoetsen'), findsOneWidget);
|
||||
expect(find.text('Toetsenlegenda'), findsOneWidget);
|
||||
expect(find.text('Scherm wisselen (meerdere schermen)'), findsOneWidget);
|
||||
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
await tester.pump();
|
||||
expect(find.text('Sneltoetsen'), findsNothing);
|
||||
expect(find.text('Toetsenlegenda'), findsNothing);
|
||||
// Esc closed the help, not the presentation.
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
@ -250,7 +257,7 @@ void main() {
|
|||
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
await tester.pump();
|
||||
expect(find.text('Slide-overzicht'), findsNothing);
|
||||
expect(find.text('1 / 2'), findsOneWidget);
|
||||
expect(find.text('Eerste'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue