145 lines
5.4 KiB
Dart
145 lines
5.4 KiB
Dart
import 'dart:io' show Platform;
|
|
import 'package:flutter/services.dart';
|
|
import 'fullscreen_state_manager.dart';
|
|
|
|
/// Abstract class for receiving macOS window delegate callbacks.
|
|
/// Extend this class and register with [MacOSWindowService] to receive
|
|
/// fullscreen transition events.
|
|
abstract class MacOSWindowDelegate {
|
|
/// Called when the window is about to enter fullscreen mode.
|
|
// ignore: no-empty-block - default no-op, subclasses override as needed
|
|
void windowWillEnterFullScreen() {}
|
|
|
|
/// Called when the window has entered fullscreen mode.
|
|
// ignore: no-empty-block - default no-op, subclasses override as needed
|
|
void windowDidEnterFullScreen() {}
|
|
|
|
/// Called when the window is about to exit fullscreen mode.
|
|
// ignore: no-empty-block - default no-op, subclasses override as needed
|
|
void windowWillExitFullScreen() {}
|
|
|
|
/// Called when the window has exited fullscreen mode.
|
|
// ignore: no-empty-block - default no-op, subclasses override as needed
|
|
void windowDidExitFullScreen() {}
|
|
}
|
|
|
|
/// Service for manipulating macOS window properties.
|
|
/// This is a native implementation replacing the macos_window_utils package.
|
|
///
|
|
/// Note: Titlebar, toolbar, and traffic light position management is now handled
|
|
/// directly in Swift (WindowDelegate.swift) during fullscreen transitions.
|
|
/// This service only exposes what's needed externally:
|
|
/// - Traffic light visibility (for video controls)
|
|
/// - Fullscreen enter/exit (for video controls)
|
|
/// - Delegate registration (for FullscreenStateManager updates)
|
|
class MacOSWindowService {
|
|
static const _channel = MethodChannel('com.plezy/window_utils');
|
|
static bool _initialized = false;
|
|
static bool _delegateEnabled = false;
|
|
static final List<MacOSWindowDelegate> _delegates = [];
|
|
static final MacOSWindowDelegate _fullscreenDelegate = _FullscreenWindowDelegate();
|
|
|
|
static Future<void> _invoke(String method, [Map<String, dynamic>? args]) async {
|
|
if (!Platform.isMacOS) return;
|
|
await _channel.invokeMethod(method, args);
|
|
}
|
|
|
|
static void _notifyDelegates(void Function(MacOSWindowDelegate) callback) {
|
|
for (final delegate in _delegates) {
|
|
callback(delegate);
|
|
}
|
|
}
|
|
|
|
static Future<dynamic> _handleMethodCall(MethodCall call) async {
|
|
switch (call.method) {
|
|
case 'windowWillEnterFullScreen':
|
|
_notifyDelegates((d) => d.windowWillEnterFullScreen());
|
|
case 'windowDidEnterFullScreen':
|
|
_notifyDelegates((d) => d.windowDidEnterFullScreen());
|
|
case 'windowWillExitFullScreen':
|
|
_notifyDelegates((d) => d.windowWillExitFullScreen());
|
|
case 'windowDidExitFullScreen':
|
|
_notifyDelegates((d) => d.windowDidExitFullScreen());
|
|
}
|
|
}
|
|
|
|
/// Initialize the window service and set up the titlebar.
|
|
///
|
|
/// Note: The initial window configuration (transparent titlebar, toolbar,
|
|
/// button positions, fullscreen presentation options) is now applied in
|
|
/// MainFlutterWindow.swift / WindowDelegate.swift BEFORE frame restoration
|
|
/// to prevent the window from shrinking on launch.
|
|
///
|
|
/// This method sets up the Dart-side callbacks for fullscreen state tracking.
|
|
static Future<void> setupCustomTitlebar() async {
|
|
if (!Platform.isMacOS) return;
|
|
|
|
if (_initialized && _delegateEnabled) {
|
|
await syncWindowChrome();
|
|
FullscreenStateManager().setFullscreen(await isFullscreen());
|
|
return;
|
|
}
|
|
|
|
await initialize(enableWindowDelegate: true);
|
|
addWindowDelegate(_fullscreenDelegate);
|
|
await syncWindowChrome();
|
|
FullscreenStateManager().setFullscreen(await isFullscreen());
|
|
}
|
|
|
|
/// Initialize the window service.
|
|
/// Must be called before using other methods.
|
|
/// Set [enableWindowDelegate] to true to receive fullscreen callbacks.
|
|
static Future<void> initialize({bool enableWindowDelegate = false}) async {
|
|
if (!Platform.isMacOS) return;
|
|
|
|
if (!_initialized || (enableWindowDelegate && !_delegateEnabled)) {
|
|
await _channel.invokeMethod('initialize', {'enableWindowDelegate': enableWindowDelegate});
|
|
_initialized = true;
|
|
}
|
|
|
|
// Set up handler if not already done and delegate is requested
|
|
if (enableWindowDelegate && !_delegateEnabled) {
|
|
_channel.setMethodCallHandler(_handleMethodCall);
|
|
_delegateEnabled = true;
|
|
}
|
|
}
|
|
|
|
static void addWindowDelegate(MacOSWindowDelegate delegate) {
|
|
if (!_delegates.contains(delegate)) {
|
|
_delegates.add(delegate);
|
|
}
|
|
}
|
|
|
|
static void removeWindowDelegate(MacOSWindowDelegate delegate) {
|
|
_delegates.remove(delegate);
|
|
}
|
|
|
|
static Future<void> setTrafficLightsVisible(bool visible) => _invoke('setTrafficLightsVisible', {'visible': visible});
|
|
|
|
static Future<void> syncWindowChrome() => _invoke('syncWindowChrome');
|
|
|
|
static Future<void> enterFullscreen() => _invoke('enterFullscreen');
|
|
|
|
static Future<void> exitFullscreen() => _invoke('exitFullscreen');
|
|
|
|
static Future<bool> isFullscreen() async {
|
|
if (!Platform.isMacOS) return false;
|
|
return await _channel.invokeMethod<bool>('isFullscreen') ?? false;
|
|
}
|
|
}
|
|
|
|
/// Internal window delegate that manages fullscreen state.
|
|
/// Note: Window manipulation (toolbar, titlebar, traffic lights) is now handled
|
|
/// directly in Swift's WindowDelegate. This class only updates Dart-side state.
|
|
class _FullscreenWindowDelegate extends MacOSWindowDelegate {
|
|
@override
|
|
void windowWillEnterFullScreen() {
|
|
FullscreenStateManager().setFullscreen(true);
|
|
}
|
|
|
|
@override
|
|
void windowDidExitFullScreen() {
|
|
FullscreenStateManager().setFullscreen(false);
|
|
}
|
|
}
|