feat: Integrate window manager for desktop windowing support and enhance input handling
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -4,7 +4,9 @@
|
|||||||
/// before presenting the game-selection flow.
|
/// before presenting the game-selection flow.
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
||||||
import 'package:wolf_3d_gui/screens/game_screen.dart';
|
import 'package:wolf_3d_gui/screens/game_screen.dart';
|
||||||
|
|
||||||
@@ -12,6 +14,10 @@ import 'package:wolf_3d_gui/screens/game_screen.dart';
|
|||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
if (_supportsDesktopWindowing) {
|
||||||
|
await windowManager.ensureInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
final Wolf3d wolf3d = await Wolf3d().init();
|
final Wolf3d wolf3d = await Wolf3d().init();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
@@ -23,6 +29,19 @@ void main() async {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get _supportsDesktopWindowing {
|
||||||
|
if (kIsWeb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (defaultTargetPlatform) {
|
||||||
|
TargetPlatform.linux ||
|
||||||
|
TargetPlatform.windows ||
|
||||||
|
TargetPlatform.macOS => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class _NoGameDataScreen extends StatelessWidget {
|
class _NoGameDataScreen extends StatelessWidget {
|
||||||
const _NoGameDataScreen();
|
const _NoGameDataScreen();
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
/// Active gameplay screen for the Flutter host.
|
/// Active gameplay screen for the Flutter host.
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
|
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
|
||||||
import 'package:wolf_3d_dart/wolf_3d_renderer.dart';
|
import 'package:wolf_3d_dart/wolf_3d_renderer.dart';
|
||||||
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
||||||
|
import 'package:wolf_3d_flutter/wolf_3d_input_flutter.dart';
|
||||||
import 'package:wolf_3d_renderer/wolf_3d_ascii_renderer.dart';
|
import 'package:wolf_3d_renderer/wolf_3d_ascii_renderer.dart';
|
||||||
import 'package:wolf_3d_renderer/wolf_3d_flutter_renderer.dart';
|
import 'package:wolf_3d_renderer/wolf_3d_flutter_renderer.dart';
|
||||||
import 'package:wolf_3d_renderer/wolf_3d_glsl_renderer.dart';
|
import 'package:wolf_3d_renderer/wolf_3d_glsl_renderer.dart';
|
||||||
@@ -16,14 +21,28 @@ enum RendererMode {
|
|||||||
hardware,
|
hardware,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef HostShortcutHandler =
|
||||||
|
bool Function(
|
||||||
|
KeyEvent event,
|
||||||
|
Wolf3dFlutterInput input,
|
||||||
|
);
|
||||||
|
|
||||||
/// Launches a [WolfEngine] via [Wolf3d] and exposes renderer/input integrations.
|
/// Launches a [WolfEngine] via [Wolf3d] and exposes renderer/input integrations.
|
||||||
class GameScreen extends StatefulWidget {
|
class GameScreen extends StatefulWidget {
|
||||||
/// Shared application facade owning the engine, audio, and input.
|
/// Shared application facade owning the engine, audio, and input.
|
||||||
final Wolf3d wolf3d;
|
final Wolf3d wolf3d;
|
||||||
|
|
||||||
|
/// Optional host-level shortcut override.
|
||||||
|
///
|
||||||
|
/// Return `true` when the event was consumed. Handlers may call
|
||||||
|
/// [Wolf3dFlutterInput.suppressActionOnce] to keep actions from reaching the
|
||||||
|
/// engine update loop.
|
||||||
|
final HostShortcutHandler? hostShortcutHandler;
|
||||||
|
|
||||||
/// Creates a gameplay screen driven by [wolf3d].
|
/// Creates a gameplay screen driven by [wolf3d].
|
||||||
const GameScreen({
|
const GameScreen({
|
||||||
required this.wolf3d,
|
required this.wolf3d,
|
||||||
|
this.hostShortcutHandler,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,6 +174,10 @@ class _GameScreenState extends State<GameScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_handleHostShortcut(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.logicalKey == widget.wolf3d.input.rendererToggleKey) {
|
if (event.logicalKey == widget.wolf3d.input.rendererToggleKey) {
|
||||||
setState(_cycleRendererMode);
|
setState(_cycleRendererMode);
|
||||||
return;
|
return;
|
||||||
@@ -218,4 +241,61 @@ class _GameScreenState extends State<GameScreen> {
|
|||||||
void _toggleGlslEffects() {
|
void _toggleGlslEffects() {
|
||||||
_glslEffectsEnabled = !_glslEffectsEnabled;
|
_glslEffectsEnabled = !_glslEffectsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _handleHostShortcut(KeyEvent event) {
|
||||||
|
final HostShortcutHandler? customHandler = widget.hostShortcutHandler;
|
||||||
|
if (customHandler != null) {
|
||||||
|
return customHandler(event, widget.wolf3d.input);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isAltEnter(event)) {
|
||||||
|
// Consume Enter so fullscreen toggling does not also activate menu items.
|
||||||
|
widget.wolf3d.input.suppressInteractOnce();
|
||||||
|
unawaited(_toggleFullscreen());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isAltEnter(KeyEvent event) {
|
||||||
|
final bool isEnter =
|
||||||
|
event.logicalKey == LogicalKeyboardKey.enter ||
|
||||||
|
event.logicalKey == LogicalKeyboardKey.numpadEnter;
|
||||||
|
if (!isEnter) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<LogicalKeyboardKey> pressedKeys =
|
||||||
|
HardwareKeyboard.instance.logicalKeysPressed;
|
||||||
|
return pressedKeys.contains(LogicalKeyboardKey.altLeft) ||
|
||||||
|
pressedKeys.contains(LogicalKeyboardKey.altRight) ||
|
||||||
|
pressedKeys.contains(LogicalKeyboardKey.alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _toggleFullscreen() async {
|
||||||
|
if (!_supportsDesktopWindowing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final bool isFullScreen = await windowManager.isFullScreen();
|
||||||
|
await windowManager.setFullScreen(!isFullScreen);
|
||||||
|
} on MissingPluginException {
|
||||||
|
// No-op on hosts where the window manager plugin is unavailable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _supportsDesktopWindowing {
|
||||||
|
if (kIsWeb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (defaultTargetPlatform) {
|
||||||
|
TargetPlatform.linux ||
|
||||||
|
TargetPlatform.windows ||
|
||||||
|
TargetPlatform.macOS => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,17 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||||
|
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||||
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
||||||
|
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
||||||
|
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
audioplayers_linux
|
audioplayers_linux
|
||||||
|
screen_retriever_linux
|
||||||
|
window_manager
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ dependencies:
|
|||||||
wolf_3d_dart:
|
wolf_3d_dart:
|
||||||
wolf_3d_renderer: any
|
wolf_3d_renderer: any
|
||||||
wolf_3d_flutter: any
|
wolf_3d_flutter: any
|
||||||
|
window_manager: ^0.5.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class Wolf3dFlutterInput extends Wolf3dInput {
|
|||||||
double _mouseDeltaY = 0.0;
|
double _mouseDeltaY = 0.0;
|
||||||
bool _previousMouseRightDown = false;
|
bool _previousMouseRightDown = false;
|
||||||
bool _queuedBack = false;
|
bool _queuedBack = false;
|
||||||
|
final Set<WolfInputAction> _suppressedActionsOnce = <WolfInputAction>{};
|
||||||
|
|
||||||
// Mouse-look is optional so touch or keyboard-only hosts can keep the same
|
// Mouse-look is optional so touch or keyboard-only hosts can keep the same
|
||||||
// adapter without incurring accidental pointer-driven movement.
|
// adapter without incurring accidental pointer-driven movement.
|
||||||
@@ -153,8 +154,21 @@ class Wolf3dFlutterInput extends Wolf3dInput {
|
|||||||
_queuedBack = true;
|
_queuedBack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Suppresses [action] for the next [update] tick only.
|
||||||
|
void suppressActionOnce(WolfInputAction action) {
|
||||||
|
_suppressedActionsOnce.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience helper for host shortcuts that consume Enter/Interact.
|
||||||
|
void suppressInteractOnce() {
|
||||||
|
suppressActionOnce(WolfInputAction.interact);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether any bound key for [action] is currently pressed.
|
/// Returns whether any bound key for [action] is currently pressed.
|
||||||
bool _isActive(WolfInputAction action, Set<LogicalKeyboardKey> pressedKeys) {
|
bool _isActive(WolfInputAction action, Set<LogicalKeyboardKey> pressedKeys) {
|
||||||
|
if (_suppressedActionsOnce.contains(action)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return bindings[action]!.any((key) => pressedKeys.contains(key));
|
return bindings[action]!.any((key) => pressedKeys.contains(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +177,9 @@ class Wolf3dFlutterInput extends Wolf3dInput {
|
|||||||
WolfInputAction action,
|
WolfInputAction action,
|
||||||
Set<LogicalKeyboardKey> newlyPressed,
|
Set<LogicalKeyboardKey> newlyPressed,
|
||||||
) {
|
) {
|
||||||
|
if (_suppressedActionsOnce.contains(action)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return bindings[action]!.any((key) => newlyPressed.contains(key));
|
return bindings[action]!.any((key) => newlyPressed.contains(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,5 +240,6 @@ class Wolf3dFlutterInput extends Wolf3dInput {
|
|||||||
_previousKeys = Set.from(pressedKeys);
|
_previousKeys = Set.from(pressedKeys);
|
||||||
_previousMouseRightDown = isMouseRightDown;
|
_previousMouseRightDown = isMouseRightDown;
|
||||||
_queuedBack = false;
|
_queuedBack = false;
|
||||||
|
_suppressedActionsOnce.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user