feat: polish app icons and presentation exports
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 464 KiB After Width: | Height: | Size: 518 KiB |
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|
|
||||||
43
ios/Podfile
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 418 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 811 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 16 KiB |
|
|
@ -25,6 +25,11 @@ class ThemeProfile {
|
||||||
/// Horizontale positie van de footer: left, center of right.
|
/// Horizontale positie van de footer: left, center of right.
|
||||||
final String footerPosition;
|
final String footerPosition;
|
||||||
|
|
||||||
|
/// Optional markdown slide that is appended when presenting/exporting with
|
||||||
|
/// this theme profile. It stays out of the editable deck slide list.
|
||||||
|
final bool closingSlideEnabled;
|
||||||
|
final String closingSlideMarkdown;
|
||||||
|
|
||||||
const ThemeProfile({
|
const ThemeProfile({
|
||||||
this.name = 'Standaard',
|
this.name = 'Standaard',
|
||||||
this.slideBackgroundColor = '#FFFFFF',
|
this.slideBackgroundColor = '#FFFFFF',
|
||||||
|
|
@ -42,6 +47,8 @@ class ThemeProfile {
|
||||||
this.footerText = '',
|
this.footerText = '',
|
||||||
this.footerShowPageNumbers = false,
|
this.footerShowPageNumbers = false,
|
||||||
this.footerPosition = 'right',
|
this.footerPosition = 'right',
|
||||||
|
this.closingSlideEnabled = false,
|
||||||
|
this.closingSlideMarkdown = '# Bedankt\n\nVragen?',
|
||||||
}) : tableTextColor = tableTextColor ?? textColor;
|
}) : tableTextColor = tableTextColor ?? textColor;
|
||||||
|
|
||||||
static const logoPositions = [
|
static const logoPositions = [
|
||||||
|
|
@ -70,6 +77,8 @@ class ThemeProfile {
|
||||||
String? footerText,
|
String? footerText,
|
||||||
bool? footerShowPageNumbers,
|
bool? footerShowPageNumbers,
|
||||||
String? footerPosition,
|
String? footerPosition,
|
||||||
|
bool? closingSlideEnabled,
|
||||||
|
String? closingSlideMarkdown,
|
||||||
bool clearLogo = false,
|
bool clearLogo = false,
|
||||||
}) {
|
}) {
|
||||||
return ThemeProfile(
|
return ThemeProfile(
|
||||||
|
|
@ -91,6 +100,8 @@ class ThemeProfile {
|
||||||
footerShowPageNumbers:
|
footerShowPageNumbers:
|
||||||
footerShowPageNumbers ?? this.footerShowPageNumbers,
|
footerShowPageNumbers ?? this.footerShowPageNumbers,
|
||||||
footerPosition: footerPosition ?? this.footerPosition,
|
footerPosition: footerPosition ?? this.footerPosition,
|
||||||
|
closingSlideEnabled: closingSlideEnabled ?? this.closingSlideEnabled,
|
||||||
|
closingSlideMarkdown: closingSlideMarkdown ?? this.closingSlideMarkdown,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +123,8 @@ class ThemeProfile {
|
||||||
'footerText': footerText,
|
'footerText': footerText,
|
||||||
'footerShowPageNumbers': footerShowPageNumbers,
|
'footerShowPageNumbers': footerShowPageNumbers,
|
||||||
'footerPosition': footerPosition,
|
'footerPosition': footerPosition,
|
||||||
|
'closingSlideEnabled': closingSlideEnabled,
|
||||||
|
'closingSlideMarkdown': closingSlideMarkdown,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,6 +153,9 @@ class ThemeProfile {
|
||||||
footerText: json['footerText'] as String? ?? '',
|
footerText: json['footerText'] as String? ?? '',
|
||||||
footerShowPageNumbers: json['footerShowPageNumbers'] as bool? ?? false,
|
footerShowPageNumbers: json['footerShowPageNumbers'] as bool? ?? false,
|
||||||
footerPosition: json['footerPosition'] as String? ?? 'right',
|
footerPosition: json['footerPosition'] as String? ?? 'right',
|
||||||
|
closingSlideEnabled: json['closingSlideEnabled'] as bool? ?? false,
|
||||||
|
closingSlideMarkdown:
|
||||||
|
json['closingSlideMarkdown'] as String? ?? '# Bedankt\n\nVragen?',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class SlideRasterizer {
|
||||||
TlpLevel tlp = TlpLevel.none,
|
TlpLevel tlp = TlpLevel.none,
|
||||||
int targetWidth = 1920,
|
int targetWidth = 1920,
|
||||||
void Function(int done, int total)? onProgress,
|
void Function(int done, int total)? onProgress,
|
||||||
|
void Function(String phase, int done, int total)? onStage,
|
||||||
}) async {
|
}) async {
|
||||||
final overlay = Overlay.of(context, rootOverlay: true);
|
final overlay = Overlay.of(context, rootOverlay: true);
|
||||||
final pixelRatio = targetWidth / logicalSize.width;
|
final pixelRatio = targetWidth / logicalSize.width;
|
||||||
|
|
@ -54,6 +55,7 @@ class SlideRasterizer {
|
||||||
final results = <Uint8List>[];
|
final results = <Uint8List>[];
|
||||||
try {
|
try {
|
||||||
for (var i = 0; i < slides.length; i++) {
|
for (var i = 0; i < slides.length; i++) {
|
||||||
|
onStage?.call('prepare', i, slides.length);
|
||||||
// Warm this slide's images immediately before capturing it. Doing it
|
// Warm this slide's images immediately before capturing it. Doing it
|
||||||
// per slide (instead of once up front) guarantees the bitmap is decoded
|
// per slide (instead of once up front) guarantees the bitmap is decoded
|
||||||
// and resident in the cache at capture time, no matter how many images
|
// and resident in the cache at capture time, no matter how many images
|
||||||
|
|
@ -66,6 +68,7 @@ class SlideRasterizer {
|
||||||
]);
|
]);
|
||||||
if (!context.mounted) break;
|
if (!context.mounted) break;
|
||||||
|
|
||||||
|
onStage?.call('render', i, slides.length);
|
||||||
final key = GlobalKey();
|
final key = GlobalKey();
|
||||||
final entry = OverlayEntry(
|
final entry = OverlayEntry(
|
||||||
builder: (_) => Positioned(
|
builder: (_) => Positioned(
|
||||||
|
|
@ -96,6 +99,7 @@ class SlideRasterizer {
|
||||||
} finally {
|
} finally {
|
||||||
entry.remove();
|
entry.remove();
|
||||||
}
|
}
|
||||||
|
onStage?.call('done', i + 1, slides.length);
|
||||||
onProgress?.call(i + 1, slides.length);
|
onProgress?.call(i + 1, slides.length);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,19 @@ List<String> _imageUsages(WidgetRef ref, String absolutePath) {
|
||||||
return usages;
|
return usages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Slide> _slidesForPresentationOrExport(Deck deck) {
|
||||||
|
final slides = deck.slides.where((s) => !s.skipped).toList();
|
||||||
|
final closingMarkdown = deck.themeProfile.closingSlideMarkdown.trim();
|
||||||
|
if (deck.themeProfile.closingSlideEnabled && closingMarkdown.isNotEmpty) {
|
||||||
|
slides.add(
|
||||||
|
Slide.create(
|
||||||
|
SlideType.freeMarkdown,
|
||||||
|
).copyWith(customMarkdown: closingMarkdown),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return slides;
|
||||||
|
}
|
||||||
|
|
||||||
// ── App shell ─────────────────────────────────────────────────────────────────
|
// ── App shell ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
class AppShell extends ConsumerStatefulWidget {
|
class AppShell extends ConsumerStatefulWidget {
|
||||||
|
|
@ -904,7 +917,8 @@ class _MainLayoutState extends ConsumerState<_MainLayout> {
|
||||||
for (var i = 0; i < deck.slides.length; i++)
|
for (var i = 0; i < deck.slides.length; i++)
|
||||||
if (!deck.slides[i].skipped) i,
|
if (!deck.slides[i].skipped) i,
|
||||||
];
|
];
|
||||||
if (visible.isEmpty) {
|
final slides = _slidesForPresentationOrExport(deck);
|
||||||
|
if (slides.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
|
|
@ -916,9 +930,10 @@ class _MainLayoutState extends ConsumerState<_MainLayout> {
|
||||||
}
|
}
|
||||||
var initial = visible.indexWhere((i) => i >= editor.selectedIndex);
|
var initial = visible.indexWhere((i) => i >= editor.selectedIndex);
|
||||||
if (initial < 0) initial = visible.length - 1;
|
if (initial < 0) initial = visible.length - 1;
|
||||||
|
if (initial < 0) initial = 0;
|
||||||
FullscreenPresenter.show(
|
FullscreenPresenter.show(
|
||||||
context,
|
context,
|
||||||
slides: [for (final i in visible) deck.slides[i]],
|
slides: slides,
|
||||||
projectPath: deck.projectPath,
|
projectPath: deck.projectPath,
|
||||||
themeProfile: deck.themeProfile,
|
themeProfile: deck.themeProfile,
|
||||||
initialIndex: initial,
|
initialIndex: initial,
|
||||||
|
|
@ -927,7 +942,7 @@ class _MainLayoutState extends ConsumerState<_MainLayout> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void exportDeck() {
|
void exportDeck() {
|
||||||
final slides = deck.slides.where((s) => !s.skipped).toList();
|
final slides = _slidesForPresentationOrExport(deck);
|
||||||
if (slides.isEmpty) {
|
if (slides.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
|
|
@ -947,7 +962,9 @@ class _MainLayoutState extends ConsumerState<_MainLayout> {
|
||||||
exportService: widget.exportService,
|
exportService: widget.exportService,
|
||||||
tlp: deck.tlp,
|
tlp: deck.tlp,
|
||||||
exportDirectory: ref.read(settingsProvider).exportDirectory,
|
exportDirectory: ref.read(settingsProvider).exportDirectory,
|
||||||
markdown: deckNotifier.generateMarkdown(),
|
markdown: ref
|
||||||
|
.read(markdownServiceProvider)
|
||||||
|
.generateDeck(deck.copyWith(slides: slides)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,12 @@ class _ExportDialogState extends State<ExportDialog> {
|
||||||
_total = needsRaster ? widget.slides.length : 0;
|
_total = needsRaster ? widget.slides.length : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Give the dialog a frame to paint before the potentially expensive first
|
||||||
|
// image decode/raster pass starts.
|
||||||
|
await WidgetsBinding.instance.endOfFrame;
|
||||||
|
await Future<void>.delayed(Duration.zero);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
final images = needsRaster
|
final images = needsRaster
|
||||||
? await SlideRasterizer.rasterize(
|
? await SlideRasterizer.rasterize(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -101,6 +107,14 @@ class _ExportDialogState extends State<ExportDialog> {
|
||||||
onProgress: (done, total) {
|
onProgress: (done, total) {
|
||||||
if (mounted) setState(() => _done = done);
|
if (mounted) setState(() => _done = done);
|
||||||
},
|
},
|
||||||
|
onStage: (phase, done, total) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_phase = _stageText(phase, done, total);
|
||||||
|
_done = done;
|
||||||
|
_total = total;
|
||||||
|
});
|
||||||
|
},
|
||||||
)
|
)
|
||||||
: const <Uint8List>[];
|
: const <Uint8List>[];
|
||||||
|
|
||||||
|
|
@ -129,6 +143,23 @@ class _ExportDialogState extends State<ExportDialog> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _stageText(String phase, int done, int total) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
final number = (done + 1).clamp(1, total);
|
||||||
|
switch (phase) {
|
||||||
|
case 'prepare':
|
||||||
|
return '${l10n.d('Slide')} $number ${l10n.d('voorbereiden…')}';
|
||||||
|
case 'render':
|
||||||
|
return '${l10n.d('Slide')} $number ${l10n.d('renderen…')}';
|
||||||
|
case 'done':
|
||||||
|
return done >= total
|
||||||
|
? l10n.d('Slides gerenderd.')
|
||||||
|
: '${l10n.d('Slide')} $done ${l10n.d('gerenderd.')}';
|
||||||
|
default:
|
||||||
|
return l10n.t('renderingSlides');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
late TextEditingController _profileName;
|
late TextEditingController _profileName;
|
||||||
late TextEditingController _logoSize;
|
late TextEditingController _logoSize;
|
||||||
late TextEditingController _footerText;
|
late TextEditingController _footerText;
|
||||||
|
late TextEditingController _closingSlideMarkdown;
|
||||||
|
|
||||||
/// Whether the user changed the active profile in this session. Used to
|
/// Whether the user changed the active profile in this session. Used to
|
||||||
/// decide whether to apply the profile to the currently open presentation.
|
/// decide whether to apply the profile to the currently open presentation.
|
||||||
|
|
@ -74,6 +75,9 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
_profileName = TextEditingController(text: _themeProfile.name);
|
_profileName = TextEditingController(text: _themeProfile.name);
|
||||||
_logoSize = TextEditingController(text: _themeProfile.logoSize.toString());
|
_logoSize = TextEditingController(text: _themeProfile.logoSize.toString());
|
||||||
_footerText = TextEditingController(text: _themeProfile.footerText);
|
_footerText = TextEditingController(text: _themeProfile.footerText);
|
||||||
|
_closingSlideMarkdown = TextEditingController(
|
||||||
|
text: _themeProfile.closingSlideMarkdown,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -81,6 +85,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
_profileName.dispose();
|
_profileName.dispose();
|
||||||
_logoSize.dispose();
|
_logoSize.dispose();
|
||||||
_footerText.dispose();
|
_footerText.dispose();
|
||||||
|
_closingSlideMarkdown.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,6 +135,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
_profileName.text = profile.name;
|
_profileName.text = profile.name;
|
||||||
_logoSize.text = profile.logoSize.toString();
|
_logoSize.text = profile.logoSize.toString();
|
||||||
_footerText.text = profile.footerText;
|
_footerText.text = profile.footerText;
|
||||||
|
_closingSlideMarkdown.text = profile.closingSlideMarkdown;
|
||||||
_profileTouched = true;
|
_profileTouched = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -142,6 +148,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
name: name.isEmpty ? 'Stijlprofiel' : name,
|
name: name.isEmpty ? 'Stijlprofiel' : name,
|
||||||
logoSize: size,
|
logoSize: size,
|
||||||
footerText: _footerText.text,
|
footerText: _footerText.text,
|
||||||
|
closingSlideMarkdown: _closingSlideMarkdown.text,
|
||||||
);
|
);
|
||||||
notifier.setHomeDirectory(_homeDirectory);
|
notifier.setHomeDirectory(_homeDirectory);
|
||||||
notifier.setExportDirectory(_exportDirectory);
|
notifier.setExportDirectory(_exportDirectory);
|
||||||
|
|
@ -295,6 +302,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
_profileName.text = profile.name;
|
_profileName.text = profile.name;
|
||||||
_logoSize.text = profile.logoSize.toString();
|
_logoSize.text = profile.logoSize.toString();
|
||||||
_footerText.text = profile.footerText;
|
_footerText.text = profile.footerText;
|
||||||
|
_closingSlideMarkdown.text = profile.closingSlideMarkdown;
|
||||||
_profileTouched = true;
|
_profileTouched = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -312,6 +320,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
_profileName.text = profile.name;
|
_profileName.text = profile.name;
|
||||||
_logoSize.text = profile.logoSize.toString();
|
_logoSize.text = profile.logoSize.toString();
|
||||||
_footerText.text = profile.footerText;
|
_footerText.text = profile.footerText;
|
||||||
|
_closingSlideMarkdown.text = profile.closingSlideMarkdown;
|
||||||
_profileTouched = true;
|
_profileTouched = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -327,6 +336,7 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
_profileName.text = created.name;
|
_profileName.text = created.name;
|
||||||
_logoSize.text = created.logoSize.toString();
|
_logoSize.text = created.logoSize.toString();
|
||||||
_footerText.text = created.footerText;
|
_footerText.text = created.footerText;
|
||||||
|
_closingSlideMarkdown.text = created.closingSlideMarkdown;
|
||||||
_profileTouched = true;
|
_profileTouched = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -690,6 +700,47 @@ class _SettingsDialogState extends ConsumerState<SettingsDialog> {
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
dense: true,
|
dense: true,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_sectionTitle(l10n.d('Laatste slide')),
|
||||||
|
SwitchListTile(
|
||||||
|
value: _themeProfile.closingSlideEnabled,
|
||||||
|
onChanged: (v) => setState(() {
|
||||||
|
_themeProfile = _themeProfile.copyWith(
|
||||||
|
closingSlideEnabled: v,
|
||||||
|
closingSlideMarkdown: _closingSlideMarkdown.text,
|
||||||
|
);
|
||||||
|
_profileTouched = true;
|
||||||
|
}),
|
||||||
|
title: Text(
|
||||||
|
l10n.d('Standaard laatste slide gebruiken'),
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
l10n.d(
|
||||||
|
'Wordt automatisch toegevoegd bij presenteren en exporteren.',
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 11, color: Color(0xFF64748B)),
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
dense: true,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: _closingSlideMarkdown,
|
||||||
|
enabled: _themeProfile.closingSlideEnabled,
|
||||||
|
minLines: 4,
|
||||||
|
maxLines: 8,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.d('Markdown voor laatste slide'),
|
||||||
|
hintText: '# Bedankt\n\nVragen?',
|
||||||
|
alignLabelWithHint: true,
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
_themeProfile = _themeProfile.copyWith(closingSlideMarkdown: value);
|
||||||
|
_profileTouched = true;
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:screen_retriever/screen_retriever.dart';
|
import 'package:screen_retriever/screen_retriever.dart';
|
||||||
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import '../../models/deck.dart';
|
import '../../models/deck.dart';
|
||||||
import '../../models/settings.dart';
|
import '../../models/settings.dart';
|
||||||
|
|
@ -124,6 +125,7 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
||||||
_clockTimer = Timer.periodic(const Duration(seconds: 1), (_) {
|
_clockTimer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||||
if (mounted && _presenterView) setState(() {});
|
if (mounted && _presenterView) setState(() {});
|
||||||
});
|
});
|
||||||
|
_enableWakeLock();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_focusNode.requestFocus();
|
_focusNode.requestFocus();
|
||||||
_loadDisplays();
|
_loadDisplays();
|
||||||
|
|
@ -136,11 +138,28 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
||||||
_advanceTimer?.cancel();
|
_advanceTimer?.cancel();
|
||||||
_clockTimer?.cancel();
|
_clockTimer?.cancel();
|
||||||
_typedTimer?.cancel();
|
_typedTimer?.cancel();
|
||||||
|
_disableWakeLock();
|
||||||
_gridScroll.dispose();
|
_gridScroll.dispose();
|
||||||
_focusNode.dispose();
|
_focusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _enableWakeLock() async {
|
||||||
|
try {
|
||||||
|
await WakelockPlus.enable();
|
||||||
|
} catch (_) {
|
||||||
|
// Best-effort: unsupported platforms should not interrupt presenting.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _disableWakeLock() async {
|
||||||
|
try {
|
||||||
|
await WakelockPlus.disable();
|
||||||
|
} catch (_) {
|
||||||
|
// Best-effort cleanup.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _scheduleAdvance() {
|
void _scheduleAdvance() {
|
||||||
_advanceTimer?.cancel();
|
_advanceTimer?.cancel();
|
||||||
_advanceTimer = null;
|
_advanceTimer = null;
|
||||||
|
|
@ -268,6 +287,7 @@ class _FullscreenPresenterState extends State<FullscreenPresenter> {
|
||||||
|
|
||||||
Future<void> _exit() async {
|
Future<void> _exit() async {
|
||||||
_advanceTimer?.cancel();
|
_advanceTimer?.cancel();
|
||||||
|
await _disableWakeLock();
|
||||||
await windowManager.setFullScreen(false);
|
await windowManager.setFullScreen(false);
|
||||||
if (mounted) Navigator.pop(context);
|
if (mounted) Navigator.pop(context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||||
COMPONENT Runtime)
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/runner/resources/app_icon.png"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}/icons"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
COMPONENT Runtime)
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,20 @@ struct _MyApplication {
|
||||||
|
|
||||||
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
||||||
|
|
||||||
|
static void set_window_icon(GtkWindow* window) {
|
||||||
|
gtk_window_set_default_icon_name(APPLICATION_ID);
|
||||||
|
|
||||||
|
g_autofree gchar* executable_path = g_file_read_link("/proc/self/exe", nullptr);
|
||||||
|
if (executable_path == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_autofree gchar* executable_dir = g_path_get_dirname(executable_path);
|
||||||
|
g_autofree gchar* icon_path =
|
||||||
|
g_build_filename(executable_dir, "data", "icons", "app_icon.png", nullptr);
|
||||||
|
gtk_window_set_icon_from_file(window, icon_path, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Called when first Flutter frame received.
|
// Called when first Flutter frame received.
|
||||||
static void first_frame_cb(MyApplication* self, FlView* view) {
|
static void first_frame_cb(MyApplication* self, FlView* view) {
|
||||||
gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
|
gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
|
||||||
|
|
@ -24,6 +38,7 @@ static void my_application_activate(GApplication* application) {
|
||||||
MyApplication* self = MY_APPLICATION(application);
|
MyApplication* self = MY_APPLICATION(application);
|
||||||
GtkWindow* window =
|
GtkWindow* window =
|
||||||
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
||||||
|
set_window_icon(window);
|
||||||
|
|
||||||
// Use a header bar when running in GNOME as this is the common style used
|
// Use a header bar when running in GNOME as this is the common style used
|
||||||
// by applications and is the setup most users will be using (e.g. Ubuntu
|
// by applications and is the setup most users will be using (e.g. Ubuntu
|
||||||
|
|
|
||||||
BIN
linux/runner/resources/app_icon.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
|
|
@ -7,20 +7,24 @@ import Foundation
|
||||||
|
|
||||||
import desktop_drop
|
import desktop_drop
|
||||||
import file_picker
|
import file_picker
|
||||||
|
import package_info_plus
|
||||||
import pasteboard
|
import pasteboard
|
||||||
import screen_retriever_macos
|
import screen_retriever_macos
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import video_player_avfoundation
|
import video_player_avfoundation
|
||||||
|
import wakelock_plus
|
||||||
import window_manager
|
import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
||||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||||
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
||||||
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin"))
|
VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin"))
|
||||||
|
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,16 @@ end
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
installer.pods_project.targets.each do |target|
|
installer.pods_project.targets.each do |target|
|
||||||
flutter_additional_macos_build_settings(target)
|
flutter_additional_macos_build_settings(target)
|
||||||
|
|
||||||
|
next unless target.name == 'video_player_avfoundation'
|
||||||
|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = 'YES'
|
||||||
|
config.build_settings['SWIFT_SUPPRESS_WARNINGS'] = 'YES'
|
||||||
|
config.build_settings['OTHER_CFLAGS'] = [
|
||||||
|
'$(inherited)',
|
||||||
|
'-Wno-deprecated-declarations',
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,78 @@
|
||||||
PODS:
|
PODS:
|
||||||
|
- desktop_drop (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- file_picker (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
|
- package_info_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- pasteboard (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- screen_retriever_macos (0.0.1):
|
- screen_retriever_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- url_launcher_macos (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- video_player_avfoundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- wakelock_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- window_manager (0.5.0):
|
||||||
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
||||||
|
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
|
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
||||||
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
|
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
|
||||||
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
|
- video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||||
|
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
||||||
|
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
desktop_drop:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
|
||||||
|
file_picker:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
||||||
FlutterMacOS:
|
FlutterMacOS:
|
||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
|
package_info_plus:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||||
|
pasteboard:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
|
||||||
screen_retriever_macos:
|
screen_retriever_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos
|
||||||
|
shared_preferences_foundation:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||||
|
url_launcher_macos:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
|
video_player_avfoundation:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
|
||||||
|
wakelock_plus:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
||||||
|
window_manager:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
desktop_drop: 10a3e6a7fa9dbe350541f2574092fecfa345a07b
|
||||||
|
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||||
|
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
||||||
|
pasteboard: b594eaf838d930b276d7a35a44a32b4f489170cb
|
||||||
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
|
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
|
||||||
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
|
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
|
||||||
|
video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52
|
||||||
|
wakelock_plus: 917609be14d812ddd9e9528876538b2263aaa03b
|
||||||
|
window_manager: b729e31d38fb04905235df9ea896128991cad99e
|
||||||
|
|
||||||
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
|
PODFILE CHECKSUM: b3f873403194e4bfbc7fa8ecc38abe8f55968093
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,7 @@
|
||||||
};
|
};
|
||||||
33CC111E2044C6BF0003C045 /* ShellScript */ = {
|
33CC111E2044C6BF0003C045 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
|
@ -589,6 +590,10 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
OTHER_CFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-Wno-deprecated-declarations",
|
||||||
|
);
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
|
|
@ -721,6 +726,10 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
OTHER_CFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-Wno-deprecated-declarations",
|
||||||
|
);
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
@ -741,6 +750,10 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
OTHER_CFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-Wno-deprecated-declarations",
|
||||||
|
);
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,14 @@ import FlutterMacOS
|
||||||
|
|
||||||
@main
|
@main
|
||||||
class AppDelegate: FlutterAppDelegate {
|
class AppDelegate: FlutterAppDelegate {
|
||||||
|
override func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
|
if let iconPath = Bundle.main.path(forResource: "AppIcon", ofType: "icns") {
|
||||||
|
NSApp.applicationIconImage = NSImage(contentsOfFile: iconPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.applicationDidFinishLaunching(notification)
|
||||||
|
}
|
||||||
|
|
||||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 518 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 844 B |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 413 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 4.5 KiB |
41
pubspec.lock
|
|
@ -493,6 +493,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
package_info_plus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus
|
||||||
|
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.1"
|
||||||
|
package_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus_platform_interface
|
||||||
|
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
pasteboard:
|
pasteboard:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -670,12 +686,11 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
screen_retriever_macos:
|
screen_retriever_macos:
|
||||||
dependency: transitive
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: screen_retriever_macos
|
path: "third_party/screen_retriever_macos"
|
||||||
sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149"
|
relative: true
|
||||||
url: "https://pub.dev"
|
source: path
|
||||||
source: hosted
|
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
screen_retriever_platform_interface:
|
screen_retriever_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
|
@ -1050,6 +1065,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.2.0"
|
version: "15.2.0"
|
||||||
|
wakelock_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: wakelock_plus
|
||||||
|
sha256: ddf3db70eaa10c37558ff817519b85d527dbd21034fd5d8e1c2e85f31588f1c1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.2"
|
||||||
|
wakelock_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock_plus_platform_interface
|
||||||
|
sha256: b13f99e992e7ae6a152e16c5559d3c07ff445b13330192662494e614ca3e7d7b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ dependencies:
|
||||||
flutter_highlight: ^0.7.0
|
flutter_highlight: ^0.7.0
|
||||||
flutter_math_fork: ^0.7.4
|
flutter_math_fork: ^0.7.4
|
||||||
highlight: ^0.7.0
|
highlight: ^0.7.0
|
||||||
|
wakelock_plus: ^1.5.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
@ -38,7 +39,13 @@ dev_dependencies:
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
xml: ^6.6.1
|
xml: ^6.6.1
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
screen_retriever_macos:
|
||||||
|
path: third_party/screen_retriever_macos
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
config:
|
||||||
|
enable-swift-package-manager: false
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
- assets/images/de-winter-wittegeheel.png
|
- assets/images/de-winter-wittegeheel.png
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ void main() {
|
||||||
footerText: 'Vertrouwelijk · {page}/{total}',
|
footerText: 'Vertrouwelijk · {page}/{total}',
|
||||||
footerShowPageNumbers: true,
|
footerShowPageNumbers: true,
|
||||||
footerPosition: 'center',
|
footerPosition: 'center',
|
||||||
|
closingSlideEnabled: true,
|
||||||
|
closingSlideMarkdown: '# Einde\n\nDank voor jullie aandacht.',
|
||||||
);
|
);
|
||||||
|
|
||||||
final markdown = service.generateDeck(
|
final markdown = service.generateDeck(
|
||||||
|
|
@ -106,6 +108,11 @@ void main() {
|
||||||
expect(deck.themeProfile.footerText, 'Vertrouwelijk · {page}/{total}');
|
expect(deck.themeProfile.footerText, 'Vertrouwelijk · {page}/{total}');
|
||||||
expect(deck.themeProfile.footerShowPageNumbers, isTrue);
|
expect(deck.themeProfile.footerShowPageNumbers, isTrue);
|
||||||
expect(deck.themeProfile.footerPosition, 'center');
|
expect(deck.themeProfile.footerPosition, 'center');
|
||||||
|
expect(deck.themeProfile.closingSlideEnabled, isTrue);
|
||||||
|
expect(
|
||||||
|
deck.themeProfile.closingSlideMarkdown,
|
||||||
|
'# Einde\n\nDank voor jullie aandacht.',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('adds logo-safe class when deck profile has logo', () {
|
test('adds logo-safe class when deck profile has logo', () {
|
||||||
|
|
|
||||||
3
third_party/screen_retriever_macos/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
* First release.
|
||||||
21
third_party/screen_retriever_macos/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022-2024 LiJianying <lijy91@foxmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
12
third_party/screen_retriever_macos/README.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# screen_retriever_macos
|
||||||
|
|
||||||
|
[![pub version][pub-image]][pub-url]
|
||||||
|
|
||||||
|
[pub-image]: https://img.shields.io/pub/v/screen_retriever_macos.svg
|
||||||
|
[pub-url]: https://pub.dev/packages/screen_retriever_macos
|
||||||
|
|
||||||
|
The macos implementation of [screen_retriever](https://pub.dev/packages/screen_retriever).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](./LICENSE)
|
||||||
151
third_party/screen_retriever_macos/macos/Classes/ScreenRetrieverMacosPlugin.swift
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
import Cocoa
|
||||||
|
import FlutterMacOS
|
||||||
|
|
||||||
|
extension NSScreen {
|
||||||
|
var displayID: CGDirectDisplayID {
|
||||||
|
return deviceDescription[NSDeviceDescriptionKey(rawValue: "NSScreenNumber")] as? CGDirectDisplayID ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public func toDictionary() -> NSDictionary {
|
||||||
|
var name: String = "";
|
||||||
|
if #available(macOS 10.15, *) {
|
||||||
|
name = self.localizedName
|
||||||
|
}
|
||||||
|
let size: NSDictionary = [
|
||||||
|
"width": self.frame.width,
|
||||||
|
"height": self.frame.height,
|
||||||
|
]
|
||||||
|
let visiblePosition: NSDictionary = [
|
||||||
|
"dx": self.visibleFrame.topLeft.x,
|
||||||
|
"dy": self.visibleFrame.topLeft.y,
|
||||||
|
]
|
||||||
|
let visibleSize: NSDictionary = [
|
||||||
|
"width": self.visibleFrame.width,
|
||||||
|
"height": self.visibleFrame.height,
|
||||||
|
]
|
||||||
|
let dict: NSDictionary = [
|
||||||
|
"id": self.displayID.description,
|
||||||
|
"name": name,
|
||||||
|
"size": size,
|
||||||
|
"visiblePosition": visiblePosition,
|
||||||
|
"visibleSize": visibleSize,
|
||||||
|
]
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSRect {
|
||||||
|
var topLeft: CGPoint {
|
||||||
|
set {
|
||||||
|
let screenFrameRect = NSScreen.screens[0].frame
|
||||||
|
origin.x = newValue.x
|
||||||
|
origin.y = screenFrameRect.height - newValue.y - size.height
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
let screenFrameRect = NSScreen.screens[0].frame
|
||||||
|
return CGPoint(x: origin.x, y: screenFrameRect.height - origin.y - size.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScreenRetrieverMacosPlugin: NSObject, FlutterPlugin,FlutterStreamHandler {
|
||||||
|
private var _eventSink: FlutterEventSink?
|
||||||
|
|
||||||
|
var externalDisplayCount:Int = 0
|
||||||
|
|
||||||
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
|
let channel = FlutterMethodChannel(name: "dev.leanflutter.plugins/screen_retriever", binaryMessenger: registrar.messenger)
|
||||||
|
let instance = ScreenRetrieverMacosPlugin()
|
||||||
|
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||||
|
let eventChannel = FlutterEventChannel(name: "dev.leanflutter.plugins/screen_retriever_event", binaryMessenger: registrar.messenger)
|
||||||
|
eventChannel.setStreamHandler(instance)
|
||||||
|
|
||||||
|
instance.externalDisplayCount = NSScreen.screens.count
|
||||||
|
instance.setupNotificationCenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||||
|
self._eventSink = events
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||||
|
self._eventSink = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupNotificationCenter() {
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(handleDisplayConnection),
|
||||||
|
name: NSApplication.didChangeScreenParametersNotification,
|
||||||
|
object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@objc func handleDisplayConnection(notification: Notification) {
|
||||||
|
if externalDisplayCount < NSScreen.screens.count {
|
||||||
|
_emitEvent("display-added")
|
||||||
|
externalDisplayCount = NSScreen.screens.count
|
||||||
|
} else if externalDisplayCount > NSScreen.screens.count {
|
||||||
|
_emitEvent("display-removed")
|
||||||
|
externalDisplayCount = NSScreen.screens.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func _emitEvent(_ eventName: String) {
|
||||||
|
guard let eventSink = self._eventSink else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let event: NSDictionary = [
|
||||||
|
"type": eventName,
|
||||||
|
]
|
||||||
|
eventSink(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
switch call.method {
|
||||||
|
case "getCursorScreenPoint":
|
||||||
|
getCursorScreenPoint(call, result: result)
|
||||||
|
break
|
||||||
|
case "getPrimaryDisplay":
|
||||||
|
getPrimaryDisplay(call, result: result)
|
||||||
|
break
|
||||||
|
case "getAllDisplays":
|
||||||
|
getAllDisplays(call, result: result)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getCursorScreenPoint(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
let currentScreen = NSScreen.main!
|
||||||
|
let mouseLocation: NSPoint = NSEvent.mouseLocation;
|
||||||
|
|
||||||
|
var visibleHeight = currentScreen.frame.maxY
|
||||||
|
for screen in NSScreen.screens {
|
||||||
|
if (visibleHeight > screen.frame.maxY) {
|
||||||
|
visibleHeight = screen.frame.maxY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let data: NSDictionary = [
|
||||||
|
"dx": mouseLocation.x,
|
||||||
|
"dy": visibleHeight - mouseLocation.y,
|
||||||
|
]
|
||||||
|
result(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getPrimaryDisplay(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
result(NSScreen.screens[0].toDictionary())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getAllDisplays(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
let data: NSDictionary = [
|
||||||
|
"displays": NSScreen.screens.map({ screen in
|
||||||
|
return screen.toDictionary()
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
result(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
third_party/screen_retriever_macos/macos/screen_retriever_macos.podspec
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#
|
||||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||||
|
# Run `pod lib lint screen_retriever_macos.podspec` to validate before publishing.
|
||||||
|
#
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'screen_retriever_macos'
|
||||||
|
s.version = '0.0.1'
|
||||||
|
s.summary = 'A new Flutter plugin project.'
|
||||||
|
s.description = <<-DESC
|
||||||
|
A new Flutter plugin project.
|
||||||
|
DESC
|
||||||
|
s.homepage = 'http://example.com'
|
||||||
|
s.license = { :file => '../LICENSE' }
|
||||||
|
s.author = { 'Your Company' => 'email@example.com' }
|
||||||
|
|
||||||
|
s.source = { :path => '.' }
|
||||||
|
s.source_files = 'Classes/**/*'
|
||||||
|
s.dependency 'FlutterMacOS'
|
||||||
|
|
||||||
|
s.platform = :osx, '10.11'
|
||||||
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||||
|
s.swift_version = '5.0'
|
||||||
|
end
|
||||||
20
third_party/screen_retriever_macos/macos/screen_retriever_macos/Package.swift
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
// swift-tools-version: 5.9
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "screen_retriever_macos",
|
||||||
|
platforms: [
|
||||||
|
.macOS("10.15")
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(name: "screen-retriever-macos", targets: ["screen_retriever_macos"])
|
||||||
|
],
|
||||||
|
dependencies: [],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "screen_retriever_macos",
|
||||||
|
dependencies: []
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
import Cocoa
|
||||||
|
import FlutterMacOS
|
||||||
|
|
||||||
|
extension NSScreen {
|
||||||
|
var displayID: CGDirectDisplayID {
|
||||||
|
return deviceDescription[NSDeviceDescriptionKey(rawValue: "NSScreenNumber")] as? CGDirectDisplayID ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public func toDictionary() -> NSDictionary {
|
||||||
|
var name: String = "";
|
||||||
|
if #available(macOS 10.15, *) {
|
||||||
|
name = self.localizedName
|
||||||
|
}
|
||||||
|
let size: NSDictionary = [
|
||||||
|
"width": self.frame.width,
|
||||||
|
"height": self.frame.height,
|
||||||
|
]
|
||||||
|
let visiblePosition: NSDictionary = [
|
||||||
|
"dx": self.visibleFrame.topLeft.x,
|
||||||
|
"dy": self.visibleFrame.topLeft.y,
|
||||||
|
]
|
||||||
|
let visibleSize: NSDictionary = [
|
||||||
|
"width": self.visibleFrame.width,
|
||||||
|
"height": self.visibleFrame.height,
|
||||||
|
]
|
||||||
|
let dict: NSDictionary = [
|
||||||
|
"id": self.displayID.description,
|
||||||
|
"name": name,
|
||||||
|
"size": size,
|
||||||
|
"visiblePosition": visiblePosition,
|
||||||
|
"visibleSize": visibleSize,
|
||||||
|
]
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSRect {
|
||||||
|
var topLeft: CGPoint {
|
||||||
|
set {
|
||||||
|
let screenFrameRect = NSScreen.screens[0].frame
|
||||||
|
origin.x = newValue.x
|
||||||
|
origin.y = screenFrameRect.height - newValue.y - size.height
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
let screenFrameRect = NSScreen.screens[0].frame
|
||||||
|
return CGPoint(x: origin.x, y: screenFrameRect.height - origin.y - size.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScreenRetrieverMacosPlugin: NSObject, FlutterPlugin,FlutterStreamHandler {
|
||||||
|
private var _eventSink: FlutterEventSink?
|
||||||
|
|
||||||
|
var externalDisplayCount:Int = 0
|
||||||
|
|
||||||
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
|
let channel = FlutterMethodChannel(name: "dev.leanflutter.plugins/screen_retriever", binaryMessenger: registrar.messenger)
|
||||||
|
let instance = ScreenRetrieverMacosPlugin()
|
||||||
|
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||||
|
let eventChannel = FlutterEventChannel(name: "dev.leanflutter.plugins/screen_retriever_event", binaryMessenger: registrar.messenger)
|
||||||
|
eventChannel.setStreamHandler(instance)
|
||||||
|
|
||||||
|
instance.externalDisplayCount = NSScreen.screens.count
|
||||||
|
instance.setupNotificationCenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||||
|
self._eventSink = events
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||||
|
self._eventSink = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupNotificationCenter() {
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(handleDisplayConnection),
|
||||||
|
name: NSApplication.didChangeScreenParametersNotification,
|
||||||
|
object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@objc func handleDisplayConnection(notification: Notification) {
|
||||||
|
if externalDisplayCount < NSScreen.screens.count {
|
||||||
|
_emitEvent("display-added")
|
||||||
|
externalDisplayCount = NSScreen.screens.count
|
||||||
|
} else if externalDisplayCount > NSScreen.screens.count {
|
||||||
|
_emitEvent("display-removed")
|
||||||
|
externalDisplayCount = NSScreen.screens.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func _emitEvent(_ eventName: String) {
|
||||||
|
guard let eventSink = self._eventSink else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let event: NSDictionary = [
|
||||||
|
"type": eventName,
|
||||||
|
]
|
||||||
|
eventSink(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
switch call.method {
|
||||||
|
case "getCursorScreenPoint":
|
||||||
|
getCursorScreenPoint(call, result: result)
|
||||||
|
break
|
||||||
|
case "getPrimaryDisplay":
|
||||||
|
getPrimaryDisplay(call, result: result)
|
||||||
|
break
|
||||||
|
case "getAllDisplays":
|
||||||
|
getAllDisplays(call, result: result)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
result(FlutterMethodNotImplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getCursorScreenPoint(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
let currentScreen = NSScreen.main!
|
||||||
|
let mouseLocation: NSPoint = NSEvent.mouseLocation;
|
||||||
|
|
||||||
|
var visibleHeight = currentScreen.frame.maxY
|
||||||
|
for screen in NSScreen.screens {
|
||||||
|
if (visibleHeight > screen.frame.maxY) {
|
||||||
|
visibleHeight = screen.frame.maxY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let data: NSDictionary = [
|
||||||
|
"dx": mouseLocation.x,
|
||||||
|
"dy": visibleHeight - mouseLocation.y,
|
||||||
|
]
|
||||||
|
result(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getPrimaryDisplay(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
result(NSScreen.screens[0].toDictionary())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getAllDisplays(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
let data: NSDictionary = [
|
||||||
|
"displays": NSScreen.screens.map({ screen in
|
||||||
|
return screen.toDictionary()
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
result(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
25
third_party/screen_retriever_macos/pubspec.yaml
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
name: screen_retriever_macos
|
||||||
|
description: macOS implementation of the screen_retriever plugin.
|
||||||
|
version: 0.2.0
|
||||||
|
repository: https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_macos
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
flutter: ">=3.3.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
screen_retriever_platform_interface: ^0.2.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
mostly_reasonable_lints: ^0.1.2
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
plugin:
|
||||||
|
implements: screen_retriever
|
||||||
|
platforms:
|
||||||
|
macos:
|
||||||
|
pluginClass: ScreenRetrieverMacosPlugin
|
||||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 363 KiB |