mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-14 11:39:17 +01:00
8.2
This commit is contained in:
@@ -68,12 +68,11 @@ void main(List<String> args) async {
|
||||
}
|
||||
|
||||
stdout.writeln("Launching game...");
|
||||
if(version.executable == null){
|
||||
var executable = await version.executable;
|
||||
if(executable == null){
|
||||
throw Exception("Missing game executable at: ${version.location.path}");
|
||||
}
|
||||
|
||||
await patchHeadless(version.executable!);
|
||||
|
||||
var serverType = getServerType(result);
|
||||
var serverHost = result["server-host"] ?? serverJson["${serverType.id}_host"];
|
||||
var serverPort = result["server-port"] ?? serverJson["${serverType.id}_port"];
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:bitsdojo_window_windows/bitsdojo_window_windows.dart'
|
||||
show WinDesktopWindow;
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
@@ -17,6 +14,8 @@ import 'package:reboot_launcher/src/ui/page/home_page.dart';
|
||||
import 'package:reboot_launcher/supabase.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
const double kDefaultWindowWidth = 1024;
|
||||
const double kDefaultWindowHeight = 1024;
|
||||
@@ -42,21 +41,21 @@ void main() async {
|
||||
Get.put(BuildController());
|
||||
Get.put(SettingsController());
|
||||
Get.put(HostingController());
|
||||
doWhenWindowReady(() {
|
||||
var controller = Get.find<SettingsController>();
|
||||
var size = Size(controller.width, controller.height);
|
||||
var window = appWindow as WinDesktopWindow;
|
||||
window.setWindowCutOnMaximize(appBarSize * 2);
|
||||
appWindow.size = size;
|
||||
if(controller.offsetX != null && controller.offsetY != null){
|
||||
appWindow.position = Offset(controller.offsetX!, controller.offsetY!);
|
||||
}else {
|
||||
appWindow.alignment = Alignment.center;
|
||||
}
|
||||
|
||||
appWindow.title = "Reboot Launcher";
|
||||
appWindow.show();
|
||||
});
|
||||
await windowManager.ensureInitialized();
|
||||
var controller = Get.find<SettingsController>();
|
||||
var size = Size(controller.width, controller.height);
|
||||
await windowManager.setSize(size);
|
||||
if(controller.offsetX != null && controller.offsetY != null){
|
||||
await windowManager.setPosition(Offset(controller.offsetX!, controller.offsetY!));
|
||||
}else {
|
||||
await windowManager.setAlignment(Alignment.center);
|
||||
};
|
||||
await Window.initialize();
|
||||
await Window.setEffect(
|
||||
effect: WindowEffect.acrylic,
|
||||
color: Colors.transparent,
|
||||
dark: SystemTheme.isDarkMode
|
||||
);
|
||||
var supabase = Supabase.instance.client;
|
||||
await supabase.from('hosts')
|
||||
.delete()
|
||||
@@ -91,6 +90,7 @@ class _RebootApplicationState extends State<RebootApplication> {
|
||||
FluentThemeData _createTheme(Brightness brightness) => FluentThemeData(
|
||||
brightness: brightness,
|
||||
accentColor: SystemTheme.accentColor.accent.toAccentColor(),
|
||||
visualDensity: VisualDensity.standard
|
||||
visualDensity: VisualDensity.standard,
|
||||
scaffoldBackgroundColor: Colors.transparent
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
|
||||
import '../model/fortnite_version.dart';
|
||||
import '../model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
|
||||
Iterable<String> getServerTypes() => ServerType.values.map((entry) => entry.id);
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@ import 'dart:io';
|
||||
|
||||
import 'package:process_run/shell.dart';
|
||||
import 'package:reboot_launcher/cli.dart';
|
||||
|
||||
import '../model/fortnite_version.dart';
|
||||
import '../util/injector.dart';
|
||||
import '../util/os.dart';
|
||||
import '../util/process.dart';
|
||||
import '../util/server.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/util/injector.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/process.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
|
||||
final List<String> _errorStrings = [
|
||||
"port 3551 failed: Connection refused",
|
||||
@@ -25,10 +24,9 @@ Future<void> startGame() async {
|
||||
await _startLauncherProcess(version);
|
||||
await _startEacProcess(version);
|
||||
|
||||
var gamePath = version.executable?.path;
|
||||
if (gamePath == null) {
|
||||
throw Exception("${version.location
|
||||
.path} no longer contains a Fortnite executable, did you delete or move it?");
|
||||
var executable = await version.executable;
|
||||
if (executable == null) {
|
||||
throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?");
|
||||
}
|
||||
|
||||
if (username == null) {
|
||||
@@ -36,7 +34,7 @@ Future<void> startGame() async {
|
||||
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
|
||||
}
|
||||
|
||||
_gameProcess = await Process.start(gamePath, createRebootArgs(username!, "", host, ""))
|
||||
_gameProcess = await Process.start(executable.path, createRebootArgs(username!, "", host, ""))
|
||||
..exitCode.then((_) => _onClose())
|
||||
..outLines.forEach((line) => _onGameOutput(line, dll, host, verbose));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
|
||||
import '../util/os.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
|
||||
const String _baseDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968021373169674/cobalt.dll";
|
||||
const String _consoleDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968095033524234/console.dll";
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:process_run/shell.dart';
|
||||
|
||||
import '../model/server_type.dart';
|
||||
import '../util/server.dart' as server;
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart' as server;
|
||||
|
||||
Future<bool> startServer(String? host, String? port, ServerType type) async {
|
||||
stdout.writeln("Starting backend server...");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_launcher/src/util/patcher.dart';
|
||||
|
||||
class FortniteVersion {
|
||||
String name;
|
||||
@@ -22,8 +24,22 @@ class FortniteVersion {
|
||||
}
|
||||
}
|
||||
|
||||
File? get executable {
|
||||
return findExecutable(location, "FortniteClient-Win64-Shipping.exe");
|
||||
Future<File?> get executable async {
|
||||
var result = findExecutable(location, "FortniteClient-Win64-Shipping-Reboot.exe");
|
||||
if(result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
var original = findExecutable(location, "FortniteClient-Win64-Shipping.exe");
|
||||
if(original == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await Future.wait([
|
||||
compute(patchMatchmaking, original),
|
||||
compute(patchHeadless, original)
|
||||
]);
|
||||
return original;
|
||||
}
|
||||
|
||||
File? get launcher {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
@@ -9,7 +8,6 @@ import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/model/game_instance.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../model/update_status.dart';
|
||||
|
||||
const String kDefaultPlayerName = "Player";
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/update_controller.dart';
|
||||
|
||||
import '../../model/game_instance.dart';
|
||||
import '../../model/update_status.dart';
|
||||
import '../../util/reboot.dart';
|
||||
import 'package:reboot_launcher/src/model/game_instance.dart';
|
||||
import 'package:reboot_launcher/src/model/update_status.dart';
|
||||
import 'package:reboot_launcher/src/util/reboot.dart';
|
||||
|
||||
|
||||
const String kDefaultServerName = "Reboot Game Server";
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import '../../model/server_type.dart';
|
||||
import '../../util/server.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
|
||||
class ServerController extends GetxController {
|
||||
static const String _kDefaultServerHost = "127.0.0.1";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -5,9 +6,8 @@ import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
import 'dart:ui';
|
||||
|
||||
import '../../util/reboot.dart';
|
||||
import 'package:reboot_launcher/src/util/reboot.dart';
|
||||
|
||||
class SettingsController extends GetxController {
|
||||
static const String _kDefaultIp = "127.0.0.1";
|
||||
@@ -49,9 +49,9 @@ class SettingsController extends GetxController {
|
||||
autoUpdate = RxBool(_storage.read("auto_update") ?? _kDefaultAutoUpdate);
|
||||
autoUpdate.listen((value) => _storage.write("auto_update", value));
|
||||
scrollingDistance = 0.0;
|
||||
firstRun = RxBool(_storage.read("fr") ?? true);
|
||||
firstRun.listen((value) => _storage.write("fr", value));
|
||||
index = RxInt(firstRun() ? 0 : 1);
|
||||
firstRun = RxBool(_storage.read("first_run") ?? true);
|
||||
firstRun.listen((value) => _storage.write("first_run", value));
|
||||
index = RxInt(firstRun() ? 3 : 0);
|
||||
}
|
||||
|
||||
TextEditingController _createController(String key, String name) {
|
||||
|
||||
@@ -6,9 +6,8 @@ import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_name_input.dart';
|
||||
|
||||
import '../../util/checks.dart';
|
||||
import '../widget/shared/file_selector.dart';
|
||||
import '../widget/shared/smart_check_box.dart';
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
|
||||
import 'dialog.dart';
|
||||
import 'dialog_button.dart';
|
||||
|
||||
|
||||
@@ -6,17 +6,16 @@ import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/util/error.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/build.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/util/build.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:universal_disk_space/universal_disk_space.dart';
|
||||
|
||||
import '../../util/checks.dart';
|
||||
import '../controller/build_controller.dart';
|
||||
import '../widget/home/build_selector.dart';
|
||||
import '../widget/home/version_name_input.dart';
|
||||
import '../widget/shared/file_selector.dart';
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/build_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/build_selector.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_name_input.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
|
||||
import 'dialog.dart';
|
||||
import 'dialog_button.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
|
||||
import 'dialog_button.dart';
|
||||
@@ -24,10 +22,6 @@ class GenericDialog extends AbstractDialog {
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
MoveWindow(
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
|
||||
ContentDialog(
|
||||
style: ContentDialogThemeData(
|
||||
padding: padding ?? const EdgeInsets.only(left: 20, right: 20, top: 15.0, bottom: 5.0)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'dialog.dart';
|
||||
|
||||
const String _unsupportedServerError = "The build you are currently using is not supported by Reboot. "
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
import 'package:sync/semaphore.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
import '../../util/server.dart';
|
||||
import '../controller/server_controller.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
|
||||
import 'dialog.dart';
|
||||
import 'dialog_button.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
|
||||
void showMessage(String text){
|
||||
showSnackbar(
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder;
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_rx/src/rx_types/rx_types.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/ui/page/launcher_page.dart';
|
||||
import 'package:reboot_launcher/src/ui/page/server_page.dart';
|
||||
import 'package:reboot_launcher/src/ui/page/settings_page.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/profile_widget.dart';
|
||||
|
||||
import '../controller/game_controller.dart';
|
||||
import '../controller/settings_controller.dart';
|
||||
import '../widget/os/window_border.dart';
|
||||
import '../widget/os/window_buttons.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/os/window_border.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/os/window_title_bar.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'hosting_page.dart';
|
||||
import 'info_page.dart';
|
||||
|
||||
@@ -25,8 +23,8 @@ class HomePage extends StatefulWidget {
|
||||
|
||||
class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepAliveClientMixin {
|
||||
static const double _kDefaultPadding = 12.0;
|
||||
static const int _kPagesLength = 5;
|
||||
|
||||
static const int _kPagesLength = 6;
|
||||
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final GlobalKey _searchKey = GlobalKey();
|
||||
final FocusNode _searchFocusNode = FocusNode();
|
||||
@@ -41,6 +39,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
windowManager.show();
|
||||
windowManager.addListener(this);
|
||||
_searchController.addListener(_onSearch);
|
||||
super.initState();
|
||||
@@ -52,7 +51,8 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
return;
|
||||
}
|
||||
|
||||
_searchItems.value = _allItems.whereType<PaneItem>()
|
||||
_searchItems.value = _allItems
|
||||
.whereType<PaneItem>()
|
||||
.where((item) => (item.title as Text).data!.toLowerCase().contains(searchValue.toLowerCase()))
|
||||
.toList()
|
||||
.cast<NavigationPaneItem>();
|
||||
@@ -84,73 +84,88 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
|
||||
@override
|
||||
void onWindowMoved() {
|
||||
_settingsController.saveWindowOffset(appWindow.position);
|
||||
windowManager.getPosition()
|
||||
.then((value) => _settingsController.saveWindowOffset(value));
|
||||
super.onWindowMoved();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Stack(
|
||||
children: [
|
||||
LayoutBuilder(
|
||||
builder: (context, specs) => Obx(() => NavigationView(
|
||||
paneBodyBuilder: (pane, body) => Padding(
|
||||
padding: const EdgeInsets.all(_kDefaultPadding),
|
||||
child: body
|
||||
return Stack(children: [
|
||||
LayoutBuilder(
|
||||
builder: (context, specs) => Obx(() => NavigationPaneTheme(
|
||||
data: NavigationPaneThemeData(
|
||||
backgroundColor: FluentTheme.of(context).micaBackgroundColor.withOpacity(0.9),
|
||||
),
|
||||
appBar: NavigationAppBar(
|
||||
title: _draggableArea,
|
||||
actions: WindowTitleBar(focused: _focused()),
|
||||
automaticallyImplyLeading: false,
|
||||
leading: _backButton
|
||||
),
|
||||
pane: NavigationPane(
|
||||
key: appKey,
|
||||
selected: _selectedIndex,
|
||||
onChanged: _onIndexChanged,
|
||||
displayMode: specs.biggest.width <= 1536 ? PaneDisplayMode.compact : PaneDisplayMode.open,
|
||||
items: _items,
|
||||
footerItems: _footerItems,
|
||||
autoSuggestBox: _autoSuggestBox,
|
||||
autoSuggestBoxReplacement: const Icon(FluentIcons.search),
|
||||
),
|
||||
onOpenSearch: () => _searchFocusNode.requestFocus(),
|
||||
transitionBuilder: (child, animation) => child
|
||||
))
|
||||
),
|
||||
if(isWin11)
|
||||
Obx(() => _focused.value ? const WindowBorder() : const SizedBox())
|
||||
]
|
||||
);
|
||||
child: NavigationView(
|
||||
paneBodyBuilder: (pane, body) => Padding(
|
||||
padding: const EdgeInsets.all(_kDefaultPadding),
|
||||
child: body
|
||||
),
|
||||
appBar: NavigationAppBar(
|
||||
height: 32,
|
||||
title: _draggableArea,
|
||||
actions: WindowTitleBar(focused: _focused()),
|
||||
automaticallyImplyLeading: false,
|
||||
leading: _backButton
|
||||
),
|
||||
pane: NavigationPane(
|
||||
key: appKey,
|
||||
selected: _selectedIndex,
|
||||
onChanged: _onIndexChanged,
|
||||
menuButton: const SizedBox(),
|
||||
displayMode: PaneDisplayMode.open,
|
||||
items: _items,
|
||||
header: ProfileWidget(),
|
||||
footerItems: _footerItems,
|
||||
autoSuggestBox: _autoSuggestBox,
|
||||
autoSuggestBoxReplacement: const Icon(FluentIcons.search),
|
||||
),
|
||||
contentShape: const RoundedRectangleBorder(),
|
||||
onOpenSearch: () => _searchFocusNode.requestFocus(),
|
||||
transitionBuilder: (child, animation) => child),
|
||||
)
|
||||
)
|
||||
),
|
||||
if (isWin11)
|
||||
Obx(() => _focused.value ? const WindowBorder() : const SizedBox())
|
||||
]);
|
||||
}
|
||||
|
||||
GestureDetector get _draggableArea => GestureDetector(
|
||||
onDoubleTap: () async => await windowManager.isMaximized() ? await windowManager.restore() : await windowManager.maximize(),
|
||||
onHorizontalDragStart: (event) => windowManager.startDragging(),
|
||||
onVerticalDragStart: (event) => windowManager.startDragging()
|
||||
);
|
||||
|
||||
Widget get _backButton => Obx(() {
|
||||
for(var entry in _navigationStatus){
|
||||
for (var entry in _navigationStatus) {
|
||||
entry.value;
|
||||
}
|
||||
|
||||
var onBack = _onBack();
|
||||
return PaneItem(
|
||||
enabled: onBack != null,
|
||||
icon: const Icon(FluentIcons.back, size: 14.0),
|
||||
body: const SizedBox.shrink(),
|
||||
).build(
|
||||
context,
|
||||
false,
|
||||
onBack,
|
||||
displayMode: PaneDisplayMode.compact
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Button(
|
||||
onPressed: onBack,
|
||||
style: ButtonStyle(
|
||||
backgroundColor: ButtonState.all(Colors.transparent),
|
||||
border: ButtonState.all(BorderSide(color: Colors.transparent))
|
||||
),
|
||||
child: const Icon(FluentIcons.back, size: 13.0)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
Function()? _onBack() {
|
||||
var navigator = _navigators[_settingsController.index.value].currentState;
|
||||
if(navigator == null || !navigator.mounted || !navigator.canPop()){
|
||||
if (navigator == null || !navigator.mounted || !navigator.canPop()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var status = _navigationStatus[_settingsController.index.value];
|
||||
if(status.value <= 0){
|
||||
if (status.value <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -165,17 +180,15 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
_settingsController.index.value = index;
|
||||
}
|
||||
|
||||
TextBox get _autoSuggestBox => TextBox(
|
||||
key: _searchKey,
|
||||
controller: _searchController,
|
||||
placeholder: 'Search',
|
||||
focusNode: _searchFocusNode
|
||||
);
|
||||
|
||||
GestureDetector get _draggableArea => GestureDetector(
|
||||
onDoubleTap: () => appWindow.maximizeOrRestore(),
|
||||
onHorizontalDragStart: (event) => appWindow.startDragging(),
|
||||
onVerticalDragStart: (event) => appWindow.startDragging()
|
||||
Widget get _autoSuggestBox => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: TextBox(
|
||||
key: _searchKey,
|
||||
controller: _searchController,
|
||||
placeholder: 'Find a setting',
|
||||
focusNode: _searchFocusNode,
|
||||
autofocus: true
|
||||
),
|
||||
);
|
||||
|
||||
int? get _selectedIndex {
|
||||
@@ -184,11 +197,12 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
return _settingsController.index();
|
||||
}
|
||||
|
||||
if(_settingsController.index() >= _allItems.length){
|
||||
if (_settingsController.index() >= _allItems.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var indexOnScreen = searchItems.indexOf(_allItems[_settingsController.index()]);
|
||||
var indexOnScreen =
|
||||
searchItems.indexOf(_allItems[_settingsController.index()]);
|
||||
if (indexOnScreen.isNegative) {
|
||||
return null;
|
||||
}
|
||||
@@ -199,36 +213,75 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
List<NavigationPaneItem> get _allItems => [..._items, ..._footerItems];
|
||||
|
||||
List<NavigationPaneItem> get _footerItems => searchValue.isNotEmpty ? [] : [
|
||||
PaneItem(
|
||||
title: const Text("Settings"),
|
||||
icon: const Icon(FluentIcons.settings),
|
||||
body: SettingsPage()
|
||||
)
|
||||
PaneItem(
|
||||
title: const Text("Downloads"),
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox.square(
|
||||
dimension: 24,
|
||||
child: Image.asset("assets/images/download.png")
|
||||
)
|
||||
),
|
||||
body: const SettingsPage()
|
||||
),
|
||||
PaneItem(
|
||||
title: const Text("Settings"),
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox.square(
|
||||
dimension: 24,
|
||||
child: Image.asset("assets/images/settings.png")
|
||||
)
|
||||
),
|
||||
body: const SettingsPage()
|
||||
)
|
||||
];
|
||||
|
||||
List<NavigationPaneItem> get _items => _searchItems() ?? [
|
||||
PaneItem(
|
||||
title: const Text("Tutorial"),
|
||||
icon: const Icon(FluentIcons.info),
|
||||
body: InfoPage(_navigators[0], _navigationStatus[0])
|
||||
),
|
||||
PaneItem(
|
||||
title: const Text("Play"),
|
||||
icon: const Icon(FluentIcons.game),
|
||||
body: LauncherPage(_navigators[1], _navigationStatus[1])
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox.square(
|
||||
dimension: 24,
|
||||
child: Image.asset("assets/images/play.png")
|
||||
)
|
||||
),
|
||||
body: LauncherPage(_navigators[0], _navigationStatus[0])
|
||||
),
|
||||
|
||||
PaneItem(
|
||||
title: const Text("Host"),
|
||||
icon: const Icon(FluentIcons.server_processes),
|
||||
body: HostingPage(_navigators[2], _navigationStatus[2])
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox.square(
|
||||
dimension: 24,
|
||||
child: Image.asset("assets/images/host.png")
|
||||
)
|
||||
),
|
||||
body: HostingPage(_navigators[1], _navigationStatus[1])
|
||||
),
|
||||
|
||||
PaneItem(
|
||||
title: const Text("Backend"),
|
||||
icon: const Icon(FluentIcons.user_window),
|
||||
body: ServerPage(_navigators[3], _navigationStatus[3])
|
||||
title: const Text("Authenticator"),
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox.square(
|
||||
dimension: 24,
|
||||
child: Image.asset("assets/images/cloud.png")
|
||||
)
|
||||
),
|
||||
body: ServerPage(_navigators[2], _navigationStatus[2])
|
||||
),
|
||||
PaneItem(
|
||||
title: const Text("Tutorial"),
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: SizedBox.square(
|
||||
dimension: 24,
|
||||
child: Image.asset("assets/images/info.png")
|
||||
)
|
||||
),
|
||||
body: InfoPage(_navigators[3], _navigationStatus[3])
|
||||
)
|
||||
];
|
||||
|
||||
String get searchValue => _searchController.text;
|
||||
|
||||
@@ -3,10 +3,10 @@ import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
|
||||
|
||||
import '../../model/update_status.dart';
|
||||
import 'package:reboot_launcher/src/model/update_status.dart';
|
||||
import 'browse_page.dart';
|
||||
|
||||
class HostingPage extends StatefulWidget {
|
||||
@@ -38,7 +38,7 @@ class _HostingPageState extends State<HostingPage> with AutomaticKeepAliveClient
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ProgressRing(),
|
||||
SizedBox(height: 16.0),
|
||||
SizedBox(height: 8.0),
|
||||
Text("Updating Reboot DLL...")
|
||||
],
|
||||
),
|
||||
@@ -97,7 +97,7 @@ class _HostPageState extends State<_HostPage> with AutomaticKeepAliveClientMixin
|
||||
child: _hostingController.updateStatus.value == UpdateStatus.error ? _updateError : _rebootGuiInfo,
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 16.0
|
||||
height: 8.0
|
||||
),
|
||||
SettingTile(
|
||||
title: "Game Server",
|
||||
@@ -135,7 +135,7 @@ class _HostPageState extends State<_HostPage> with AutomaticKeepAliveClientMixin
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Version",
|
||||
@@ -163,7 +163,7 @@ class _HostPageState extends State<_HostPage> with AutomaticKeepAliveClientMixin
|
||||
]
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Browse available servers",
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
|
||||
import '../../util/os.dart';
|
||||
import '../controller/game_controller.dart';
|
||||
import '../controller/settings_controller.dart';
|
||||
import '../dialog/dialog.dart';
|
||||
import '../dialog/dialog_button.dart';
|
||||
import '../widget/home/version_selector.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
|
||||
|
||||
class InfoPage extends StatefulWidget {
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
@@ -219,7 +215,7 @@ class _PlayPageState extends State<_PlayPage> {
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: '2. Download Fortnite',
|
||||
@@ -255,7 +251,7 @@ class _PlayPageState extends State<_PlayPage> {
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: _remoteGameServerStream.stream,
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/material.dart' show Icons;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
import 'package:reboot_launcher/src/ui/page/browse_page.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
import 'package:reboot_launcher/src/ui/page/play_page.dart';
|
||||
|
||||
import '../../util/checks.dart';
|
||||
import '../../util/os.dart';
|
||||
|
||||
class LauncherPage extends StatefulWidget {
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
@@ -48,185 +36,11 @@ class _LauncherPageState extends State<LauncherPage> with AutomaticKeepAliveClie
|
||||
Widget _createScreen(String? name) {
|
||||
switch(name){
|
||||
case "home":
|
||||
return _GamePage(widget.navigatorKey, widget.nestedNavigation);
|
||||
return PlayPage(widget.navigatorKey, widget.nestedNavigation);
|
||||
case "browse":
|
||||
return const BrowsePage();
|
||||
default:
|
||||
throw Exception("Unknown page: $name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _GamePage extends StatefulWidget {
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
final RxInt nestedNavigation;
|
||||
const _GamePage(this.navigatorKey, this.nestedNavigation, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_GamePage> createState() => _GamePageState();
|
||||
}
|
||||
|
||||
class _GamePageState extends State<_GamePage> {
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
late final RxBool _showPasswordTrailing = RxBool(_gameController.password.text.isNotEmpty);
|
||||
final StreamController _matchmakingStream = StreamController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_gameController.password.addListener(() => _matchmakingStream.add(null));
|
||||
_settingsController.matchmakingIp.addListener(() => _matchmakingStream.add(null));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
SettingTile(
|
||||
title: "Credentials",
|
||||
subtitle: "Your in-game login credentials",
|
||||
expandedContentSpacing: 0,
|
||||
expandedContent: [
|
||||
SettingTile(
|
||||
title: "Username",
|
||||
subtitle: "The username that other players will see when you are in game",
|
||||
isChild: true,
|
||||
content: TextFormBox(
|
||||
placeholder: "Username",
|
||||
controller: _gameController.username,
|
||||
autovalidateMode: AutovalidateMode.always
|
||||
),
|
||||
),
|
||||
SettingTile(
|
||||
title: "Password",
|
||||
subtitle: "The password of your account, only used if the backend requires it",
|
||||
isChild: true,
|
||||
content: Obx(() => TextFormBox(
|
||||
placeholder: "Password",
|
||||
controller: _gameController.password,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
obscureText: !_gameController.showPassword.value,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
onChanged: (text) => _showPasswordTrailing.value = text.isNotEmpty,
|
||||
suffix: Button(
|
||||
onPressed: () => _gameController.showPassword.value = !_gameController.showPassword.value,
|
||||
style: ButtonStyle(
|
||||
shape: ButtonState.all(const CircleBorder()),
|
||||
backgroundColor: ButtonState.all(Colors.transparent)
|
||||
),
|
||||
child: Icon(
|
||||
_gameController.showPassword.value ? Icons.visibility_off : Icons.visibility,
|
||||
color: _showPasswordTrailing.value ? null : Colors.transparent
|
||||
),
|
||||
)
|
||||
))
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: _matchmakingStream.stream,
|
||||
builder: (context, value) =>
|
||||
SettingTile(
|
||||
title: "Matchmaking host",
|
||||
subtitle: "Enter the IP address of the game server hosting the match",
|
||||
content: TextFormBox(
|
||||
placeholder: "IP:PORT",
|
||||
controller: _settingsController.matchmakingIp,
|
||||
validator: checkMatchmaking,
|
||||
autovalidateMode: AutovalidateMode.always
|
||||
),
|
||||
expandedContent: [
|
||||
SettingTile(
|
||||
title: "Automatically start game server",
|
||||
subtitle: "This option is available when the matchmaker is set to localhost",
|
||||
contentWidth: null,
|
||||
content: !isLocalHost(_settingsController.matchmakingIp.text) || _gameController.password.text.isNotEmpty ? _disabledAutoGameServerSwitch : _autoGameServerSwitch,
|
||||
isChild: true
|
||||
),
|
||||
SettingTile(
|
||||
title: "Browse available servers",
|
||||
subtitle: "Discover new game servers that fit your play-style",
|
||||
content: Button(
|
||||
onPressed: () {
|
||||
widget.navigatorKey.currentState?.pushNamed('browse');
|
||||
widget.nestedNavigation.value += 1;
|
||||
},
|
||||
child: const Text("Browse")
|
||||
),
|
||||
isChild: true
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Version",
|
||||
subtitle: "Select the version of Fortnite you want to play",
|
||||
content: const VersionSelector(),
|
||||
expandedContent: [
|
||||
SettingTile(
|
||||
title: "Add a version from this PC's local storage",
|
||||
subtitle: "Versions coming from your local disk are not guaranteed to work",
|
||||
content: Button(
|
||||
onPressed: () => VersionSelector.openAddDialog(context),
|
||||
child: const Text("Add build"),
|
||||
),
|
||||
isChild: true
|
||||
),
|
||||
SettingTile(
|
||||
title: "Download any version from the cloud",
|
||||
subtitle: "A curated list of supported versions by Project Reboot",
|
||||
content: Button(
|
||||
onPressed: () => VersionSelector.openDownloadDialog(context),
|
||||
child: const Text("Download"),
|
||||
),
|
||||
isChild: true
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
const LaunchButton(
|
||||
host: false
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
Widget get _disabledAutoGameServerSwitch => Container(
|
||||
foregroundDecoration: const BoxDecoration(
|
||||
color: Colors.grey,
|
||||
backgroundBlendMode: BlendMode.saturation,
|
||||
),
|
||||
child: _autoGameServerSwitch,
|
||||
);
|
||||
|
||||
Widget get _autoGameServerSwitch => Obx(() => ToggleSwitch(
|
||||
checked: _gameController.autoStartGameServer() && isLocalHost(_settingsController.matchmakingIp.text) && _gameController.password.text.isEmpty,
|
||||
onChanged: (value) {
|
||||
if(!isLocalHost(_settingsController.matchmakingIp.text)){
|
||||
showMessage("This option isn't available when the matchmaker isn't set to 127.0.0.1");
|
||||
return;
|
||||
}
|
||||
|
||||
if(_gameController.password.text.isNotEmpty){
|
||||
showMessage("This option isn't available when the password isn't empty(LawinV2)");
|
||||
return;
|
||||
}
|
||||
|
||||
_gameController.autoStartGameServer.value = value;
|
||||
}
|
||||
));
|
||||
}
|
||||
154
lib/src/ui/page/play_page.dart
Normal file
154
lib/src/ui/page/play_page.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_rx/src/rx_types/rx_types.dart';
|
||||
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
|
||||
|
||||
class PlayPage extends StatefulWidget {
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
final RxInt nestedNavigation;
|
||||
const PlayPage(this.navigatorKey, this.nestedNavigation, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PlayPage> createState() => _PlayPageState();
|
||||
}
|
||||
|
||||
class _PlayPageState extends State<PlayPage> {
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final StreamController _matchmakingStream = StreamController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_gameController.password.addListener(() => _matchmakingStream.add(null));
|
||||
_settingsController.matchmakingIp.addListener(() =>
|
||||
_matchmakingStream.add(null));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
SettingTile(
|
||||
title: "Version",
|
||||
subtitle: "Select the version of Fortnite you want to play",
|
||||
content: const VersionSelector(),
|
||||
expandedContent: [
|
||||
SettingTile(
|
||||
title: "Add a version from this PC's local storage",
|
||||
subtitle: "Versions coming from your local disk are not guaranteed to work",
|
||||
content: Button(
|
||||
onPressed: () =>
|
||||
VersionSelector.openAddDialog(context),
|
||||
child: const Text("Add build"),
|
||||
),
|
||||
isChild: true
|
||||
),
|
||||
SettingTile(
|
||||
title: "Download any version from the cloud",
|
||||
subtitle: "A curated list of supported versions by Project Reboot",
|
||||
content: Button(
|
||||
onPressed: () =>
|
||||
VersionSelector.openDownloadDialog(context),
|
||||
child: const Text("Download"),
|
||||
),
|
||||
isChild: true
|
||||
)
|
||||
]
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: _matchmakingStream.stream,
|
||||
builder: (context, value) =>
|
||||
SettingTile(
|
||||
title: "Matchmaking host",
|
||||
subtitle: "Enter the IP address of the game server hosting the match",
|
||||
content: TextFormBox(
|
||||
placeholder: "IP:PORT",
|
||||
controller: _settingsController.matchmakingIp,
|
||||
validator: checkMatchmaking,
|
||||
autovalidateMode: AutovalidateMode.always
|
||||
),
|
||||
expandedContent: [
|
||||
SettingTile(
|
||||
title: "Automatically start game server",
|
||||
subtitle: "This option is available when the matchmaker is set to localhost",
|
||||
contentWidth: null,
|
||||
content: !isLocalHost(
|
||||
_settingsController.matchmakingIp.text) ||
|
||||
_gameController.password.text.isNotEmpty
|
||||
? _disabledAutoGameServerSwitch
|
||||
: _autoGameServerSwitch,
|
||||
isChild: true
|
||||
),
|
||||
SettingTile(
|
||||
title: "Browse available servers",
|
||||
subtitle: "Discover new game servers that fit your play-style",
|
||||
content: Button(
|
||||
onPressed: () {
|
||||
widget.navigatorKey.currentState
|
||||
?.pushNamed('browse');
|
||||
widget.nestedNavigation.value += 1;
|
||||
},
|
||||
child: const Text("Browse")
|
||||
),
|
||||
isChild: true
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
const LaunchButton(
|
||||
host: false
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
Widget get _disabledAutoGameServerSwitch => Container(
|
||||
foregroundDecoration: const BoxDecoration(
|
||||
color: Colors.grey,
|
||||
backgroundBlendMode: BlendMode.saturation,
|
||||
),
|
||||
child: _autoGameServerSwitch,
|
||||
);
|
||||
|
||||
Widget get _autoGameServerSwitch => Obx(() => ToggleSwitch(
|
||||
checked: _gameController.autoStartGameServer() &&
|
||||
isLocalHost(_settingsController.matchmakingIp.text) &&
|
||||
_gameController.password.text.isEmpty,
|
||||
onChanged: (value) {
|
||||
if (!isLocalHost(_settingsController.matchmakingIp.text)) {
|
||||
showMessage(
|
||||
"This option isn't available when the matchmaker isn't set to 127.0.0.1");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameController.password.text.isNotEmpty) {
|
||||
showMessage(
|
||||
"This option isn't available when the password isn't empty(LawinV2)");
|
||||
return;
|
||||
}
|
||||
|
||||
_gameController.autoStartGameServer.value = value;
|
||||
}
|
||||
));
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/server/server_type_selector.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/server/server_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/server/server_type_selector.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../dialog/dialog.dart';
|
||||
import '../dialog/dialog_button.dart';
|
||||
import '../widget/home/setting_tile.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
|
||||
class ServerPage extends StatefulWidget {
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
@@ -43,7 +43,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Host",
|
||||
@@ -55,7 +55,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Port",
|
||||
@@ -67,7 +67,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Type",
|
||||
@@ -75,7 +75,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
content: ServerTypeSelector()
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Detached",
|
||||
@@ -87,7 +87,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
))
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Server files",
|
||||
@@ -98,7 +98,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Reset Backend",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
@@ -7,12 +6,10 @@ import 'package:reboot_launcher/src/ui/dialog/dialog_button.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
|
||||
import '../../util/checks.dart';
|
||||
import '../../util/os.dart';
|
||||
import '../../util/selector.dart';
|
||||
import '../dialog/dialog.dart';
|
||||
import '../widget/home/setting_tile.dart';
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
|
||||
|
||||
class SettingsPage extends StatefulWidget {
|
||||
const SettingsPage({Key? key}) : super(key: key);
|
||||
@@ -56,7 +53,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Automatic updates",
|
||||
@@ -82,7 +79,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
|
||||
]
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Custom launch arguments",
|
||||
@@ -93,7 +90,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Create a bug report",
|
||||
@@ -104,7 +101,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Reset settings",
|
||||
@@ -134,7 +131,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
|
||||
)
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
height: 8.0,
|
||||
),
|
||||
SettingTile(
|
||||
title: "Version status",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/build_controller.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_build.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/build_controller.dart';
|
||||
|
||||
class BuildSelector extends StatefulWidget {
|
||||
final Function() onSelected;
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:process_run/shell.dart';
|
||||
import 'package:reboot_launcher/src/../main.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/model/game_instance.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/game_dialogs.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/server_dialogs.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/injector.dart';
|
||||
import 'package:reboot_launcher/src/util/patcher.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'package:reboot_launcher/src/../main.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
import 'package:reboot_launcher/src/model/game_instance.dart';
|
||||
import 'package:reboot_launcher/src/util/injector.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
import '../../../util/process.dart';
|
||||
import 'package:reboot_launcher/src/util/process.dart';
|
||||
|
||||
class LaunchButton extends StatefulWidget {
|
||||
final bool host;
|
||||
@@ -124,7 +121,8 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
|
||||
try {
|
||||
var version = _gameController.selectedVersion!;
|
||||
if(version.executable?.path == null){
|
||||
var executable = await version.executable;
|
||||
if(executable == null){
|
||||
showMissingBuildError(version);
|
||||
_onStop(widget.host);
|
||||
return;
|
||||
@@ -136,9 +134,6 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
return;
|
||||
}
|
||||
|
||||
await compute(patchHeadless, version.executable!);
|
||||
// Is this needed? await compute(patchMatchmaking, version.executable!);
|
||||
|
||||
var automaticallyStartedServer = await _startMatchMakingServer();
|
||||
await _startGameProcesses(version, widget.host, automaticallyStartedServer);
|
||||
|
||||
@@ -156,7 +151,14 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
_setStarted(host, true);
|
||||
var launcherProcess = await _createLauncherProcess(version);
|
||||
var eacProcess = await _createEacProcess(version);
|
||||
var gameProcess = await _createGameProcess(version.executable!.path, host);
|
||||
var executable = await version.executable;
|
||||
if(executable == null){
|
||||
showMissingBuildError(version);
|
||||
_onStop(widget.host);
|
||||
return;
|
||||
}
|
||||
|
||||
var gameProcess = await _createGameProcess(executable.path, host);
|
||||
var watchDogProcess = _createWatchdogProcess(gameProcess, launcherProcess, eacProcess);
|
||||
var instance = GameInstance(gameProcess, launcherProcess, eacProcess, watchDogProcess, hasChildServer);
|
||||
if(host){
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/fluent_card.dart';
|
||||
|
||||
class SettingTile extends StatefulWidget {
|
||||
static const double kDefaultContentWidth = 200.0;
|
||||
@@ -42,19 +41,17 @@ class _SettingTileState extends State<SettingTile> {
|
||||
return _contentCard;
|
||||
}
|
||||
|
||||
return Mica(
|
||||
elevation: 1,
|
||||
child: Expander(
|
||||
initiallyExpanded: true,
|
||||
contentBackgroundColor: FluentTheme.of(context).menuColor,
|
||||
headerShape: (open) => const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(4.0)),
|
||||
),
|
||||
header: _header,
|
||||
headerHeight: widget.expandedContentHeaderHeight,
|
||||
trailing: _trailing,
|
||||
content: _content
|
||||
),
|
||||
return Expander(
|
||||
initiallyExpanded: true,
|
||||
headerShape: (open) => const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(4.0)),
|
||||
),
|
||||
header: SizedBox(
|
||||
height: widget.expandedContentHeaderHeight,
|
||||
child: _header
|
||||
),
|
||||
trailing: _trailing,
|
||||
content: _content
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,8 +87,9 @@ class _SettingTileState extends State<SettingTile> {
|
||||
);
|
||||
}
|
||||
|
||||
return FluentCard(
|
||||
child: _contentCardBody,
|
||||
return Card(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)),
|
||||
child: _contentCardBody
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,18 @@ import 'dart:io';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/add_local_version.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/add_server_version.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog_button.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/add_local_version.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/smart_check_box.dart';
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:reboot_launcher/src/ui/dialog/add_server_version.dart';
|
||||
import 'package:reboot_launcher/src/util/checks.dart';
|
||||
import '../shared/file_selector.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
|
||||
|
||||
class VersionSelector extends StatefulWidget {
|
||||
const VersionSelector({Key? key}) : super(key: key);
|
||||
|
||||
126
lib/src/ui/widget/os/icons.dart
Normal file
126
lib/src/ui/widget/os/icons.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
// Switched to CustomPaint icons by https://github.com/esDotDev
|
||||
|
||||
/// Close
|
||||
class CloseIcon extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
const CloseIcon({Key? key, required this.color}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Stack(children: [
|
||||
// Use rotated containers instead of a painter because it renders slightly crisper than a painter for some reason.
|
||||
Transform.rotate(
|
||||
angle: pi * .25,
|
||||
child:
|
||||
Center(child: Container(width: 14, height: 1, color: color))),
|
||||
Transform.rotate(
|
||||
angle: pi * -.25,
|
||||
child:
|
||||
Center(child: Container(width: 14, height: 1, color: color))),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
/// Maximize
|
||||
class MaximizeIcon extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
const MaximizeIcon({Key? key, required this.color}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _AlignedPaint(_MaximizePainter(color));
|
||||
}
|
||||
|
||||
class _MaximizePainter extends _IconPainter {
|
||||
_MaximizePainter(Color color) : super(color);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Paint p = getPaint(color);
|
||||
canvas.drawRect(Rect.fromLTRB(0, 0, size.width - 1, size.height - 1), p);
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore
|
||||
class RestoreIcon extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
const RestoreIcon({
|
||||
Key? key,
|
||||
required this.color,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _AlignedPaint(_RestorePainter(color));
|
||||
}
|
||||
|
||||
class _RestorePainter extends _IconPainter {
|
||||
_RestorePainter(Color color) : super(color);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Paint p = getPaint(color);
|
||||
canvas.drawRect(Rect.fromLTRB(0, 2, size.width - 2, size.height), p);
|
||||
canvas.drawLine(const Offset(2, 2), const Offset(2, 0), p);
|
||||
canvas.drawLine(const Offset(2, 0), Offset(size.width, 0), p);
|
||||
canvas.drawLine(
|
||||
Offset(size.width, 0), Offset(size.width, size.height - 2), p);
|
||||
canvas.drawLine(Offset(size.width, size.height - 2),
|
||||
Offset(size.width - 2, size.height - 2), p);
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimize
|
||||
class MinimizeIcon extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
const MinimizeIcon({Key? key, required this.color}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _AlignedPaint(_MinimizePainter(color));
|
||||
}
|
||||
|
||||
class _MinimizePainter extends _IconPainter {
|
||||
_MinimizePainter(Color color) : super(color);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Paint p = getPaint(color);
|
||||
canvas.drawLine(
|
||||
Offset(0, size.height / 2), Offset(size.width, size.height / 2), p);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helpers
|
||||
abstract class _IconPainter extends CustomPainter {
|
||||
_IconPainter(this.color);
|
||||
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
class _AlignedPaint extends StatelessWidget {
|
||||
const _AlignedPaint(this.painter, {Key? key}) : super(key: key);
|
||||
final CustomPainter painter;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: CustomPaint(size: const Size(10, 10), painter: painter));
|
||||
}
|
||||
}
|
||||
|
||||
Paint getPaint(Color color, [bool isAntiAlias = false]) => Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.stroke
|
||||
..isAntiAlias = isAntiAlias
|
||||
..strokeWidth = 1;
|
||||
74
lib/src/ui/widget/os/mouse_state_builder.dart
Normal file
74
lib/src/ui/widget/os/mouse_state_builder.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
typedef MouseStateBuilderCB = Widget Function(
|
||||
BuildContext context, MouseState mouseState);
|
||||
|
||||
class MouseState {
|
||||
bool isMouseOver = false;
|
||||
bool isMouseDown = false;
|
||||
|
||||
MouseState();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "isMouseDown: $isMouseDown - isMouseOver: $isMouseOver";
|
||||
}
|
||||
}
|
||||
|
||||
class MouseStateBuilder extends StatefulWidget {
|
||||
final MouseStateBuilderCB builder;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
const MouseStateBuilder({Key? key, required this.builder, this.onPressed})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<MouseStateBuilder> createState() => _MouseStateBuilderState();
|
||||
}
|
||||
|
||||
class _MouseStateBuilderState extends State<MouseStateBuilder> {
|
||||
late MouseState _mouseState;
|
||||
|
||||
_MouseStateBuilderState() {
|
||||
_mouseState = MouseState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (event) {
|
||||
setState(() {
|
||||
_mouseState.isMouseOver = true;
|
||||
});
|
||||
},
|
||||
onExit: (event) {
|
||||
setState(() {
|
||||
_mouseState.isMouseOver = false;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTapDown: (_) {
|
||||
setState(() {
|
||||
_mouseState.isMouseDown = true;
|
||||
});
|
||||
},
|
||||
onTapCancel: () {
|
||||
setState(() {
|
||||
_mouseState.isMouseDown = false;
|
||||
});
|
||||
},
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_mouseState.isMouseDown = false;
|
||||
_mouseState.isMouseOver = false;
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (widget.onPressed != null) {
|
||||
widget.onPressed!();
|
||||
}
|
||||
});
|
||||
},
|
||||
onTapUp: (_) {},
|
||||
child: widget.builder(context, _mouseState)));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
@@ -10,18 +9,19 @@ class WindowBorder extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return IgnorePointer(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 1 / appWindow.scaleFactor
|
||||
padding: const EdgeInsets.only(
|
||||
top: 1
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: SystemTheme.accentColor.accent,
|
||||
width: appBarSize.toDouble()
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: SystemTheme.accentColor.accent,
|
||||
width: appBarSize.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
));
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
193
lib/src/ui/widget/os/window_button.dart
Normal file
193
lib/src/ui/widget/os/window_button.dart
Normal file
@@ -0,0 +1,193 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'icons.dart';
|
||||
import 'mouse_state_builder.dart';
|
||||
|
||||
typedef WindowButtonIconBuilder = Widget Function(
|
||||
WindowButtonContext buttonContext);
|
||||
typedef WindowButtonBuilder = Widget Function(
|
||||
WindowButtonContext buttonContext, Widget icon);
|
||||
|
||||
class WindowButtonContext {
|
||||
BuildContext context;
|
||||
MouseState mouseState;
|
||||
Color? backgroundColor;
|
||||
Color iconColor;
|
||||
|
||||
WindowButtonContext(
|
||||
{required this.context,
|
||||
required this.mouseState,
|
||||
this.backgroundColor,
|
||||
required this.iconColor});
|
||||
}
|
||||
|
||||
class WindowButtonColors {
|
||||
late Color normal;
|
||||
late Color mouseOver;
|
||||
late Color mouseDown;
|
||||
late Color iconNormal;
|
||||
late Color iconMouseOver;
|
||||
late Color iconMouseDown;
|
||||
|
||||
WindowButtonColors(
|
||||
{Color? normal,
|
||||
Color? mouseOver,
|
||||
Color? mouseDown,
|
||||
Color? iconNormal,
|
||||
Color? iconMouseOver,
|
||||
Color? iconMouseDown}) {
|
||||
this.normal = normal ?? _defaultButtonColors.normal;
|
||||
this.mouseOver = mouseOver ?? _defaultButtonColors.mouseOver;
|
||||
this.mouseDown = mouseDown ?? _defaultButtonColors.mouseDown;
|
||||
this.iconNormal = iconNormal ?? _defaultButtonColors.iconNormal;
|
||||
this.iconMouseOver = iconMouseOver ?? _defaultButtonColors.iconMouseOver;
|
||||
this.iconMouseDown = iconMouseDown ?? _defaultButtonColors.iconMouseDown;
|
||||
}
|
||||
}
|
||||
|
||||
final _defaultButtonColors = WindowButtonColors(
|
||||
normal: Colors.transparent,
|
||||
iconNormal: const Color(0xFF805306),
|
||||
mouseOver: const Color(0xFF404040),
|
||||
mouseDown: const Color(0xFF202020),
|
||||
iconMouseOver: const Color(0xFFFFFFFF),
|
||||
iconMouseDown: const Color(0xFFF0F0F0));
|
||||
|
||||
class WindowButton extends StatelessWidget {
|
||||
final WindowButtonBuilder? builder;
|
||||
final WindowButtonIconBuilder? iconBuilder;
|
||||
late final WindowButtonColors colors;
|
||||
final bool animate;
|
||||
final EdgeInsets? padding;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
WindowButton(
|
||||
{Key? key,
|
||||
WindowButtonColors? colors,
|
||||
this.builder,
|
||||
@required this.iconBuilder,
|
||||
this.padding,
|
||||
this.onPressed,
|
||||
this.animate = false})
|
||||
: super(key: key) {
|
||||
this.colors = colors ?? _defaultButtonColors;
|
||||
}
|
||||
|
||||
Color getBackgroundColor(MouseState mouseState) {
|
||||
if (mouseState.isMouseDown) return colors.mouseDown;
|
||||
if (mouseState.isMouseOver) return colors.mouseOver;
|
||||
return colors.normal;
|
||||
}
|
||||
|
||||
Color getIconColor(MouseState mouseState) {
|
||||
if (mouseState.isMouseDown) return colors.iconMouseDown;
|
||||
if (mouseState.isMouseOver) return colors.iconMouseOver;
|
||||
return colors.iconNormal;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseStateBuilder(
|
||||
builder: (context, mouseState) {
|
||||
WindowButtonContext buttonContext = WindowButtonContext(
|
||||
mouseState: mouseState,
|
||||
context: context,
|
||||
backgroundColor: getBackgroundColor(mouseState),
|
||||
iconColor: getIconColor(mouseState));
|
||||
|
||||
var icon =
|
||||
(iconBuilder != null) ? iconBuilder!(buttonContext) : Container();
|
||||
var fadeOutColor =
|
||||
getBackgroundColor(MouseState()..isMouseOver = true)
|
||||
.withOpacity(0);
|
||||
var padding = this.padding ?? EdgeInsets.zero;
|
||||
var animationMs = mouseState.isMouseOver
|
||||
? (animate ? 100 : 0)
|
||||
: (animate ? 200 : 0);
|
||||
Widget iconWithPadding = Padding(padding: padding, child: icon);
|
||||
iconWithPadding = AnimatedContainer(
|
||||
curve: Curves.easeOut,
|
||||
duration: Duration(milliseconds: animationMs),
|
||||
color: buttonContext.backgroundColor ?? fadeOutColor,
|
||||
child: iconWithPadding);
|
||||
var button = (builder != null)
|
||||
? builder!(buttonContext, icon)
|
||||
: iconWithPadding;
|
||||
return SizedBox.square(dimension: 45, child: button);
|
||||
},
|
||||
onPressed: onPressed);
|
||||
}
|
||||
}
|
||||
|
||||
class MinimizeWindowButton extends WindowButton {
|
||||
MinimizeWindowButton(
|
||||
{Key? key,
|
||||
WindowButtonColors? colors,
|
||||
VoidCallback? onPressed,
|
||||
bool? animate})
|
||||
: super(
|
||||
key: key,
|
||||
colors: colors,
|
||||
animate: animate ?? false,
|
||||
iconBuilder: (buttonContext) =>
|
||||
MinimizeIcon(color: buttonContext.iconColor),
|
||||
onPressed: onPressed ?? () => windowManager.minimize());
|
||||
}
|
||||
|
||||
class MaximizeWindowButton extends WindowButton {
|
||||
MaximizeWindowButton(
|
||||
{Key? key,
|
||||
WindowButtonColors? colors,
|
||||
VoidCallback? onPressed,
|
||||
bool? animate})
|
||||
: super(
|
||||
key: key,
|
||||
colors: colors,
|
||||
animate: animate ?? false,
|
||||
iconBuilder: (buttonContext) =>
|
||||
MaximizeIcon(color: buttonContext.iconColor),
|
||||
onPressed: onPressed ??
|
||||
() async => await windowManager.isMaximized()
|
||||
? await windowManager.restore()
|
||||
: await windowManager.maximize());
|
||||
}
|
||||
|
||||
class RestoreWindowButton extends WindowButton {
|
||||
RestoreWindowButton(
|
||||
{Key? key,
|
||||
WindowButtonColors? colors,
|
||||
VoidCallback? onPressed,
|
||||
bool? animate})
|
||||
: super(
|
||||
key: key,
|
||||
colors: colors,
|
||||
animate: animate ?? false,
|
||||
iconBuilder: (buttonContext) =>
|
||||
RestoreIcon(color: buttonContext.iconColor),
|
||||
onPressed: onPressed ??
|
||||
() async => await windowManager.isMaximized()
|
||||
? await windowManager.restore()
|
||||
: await windowManager.maximize());
|
||||
}
|
||||
|
||||
final _defaultCloseButtonColors = WindowButtonColors(
|
||||
mouseOver: const Color(0xFFD32F2F),
|
||||
mouseDown: const Color(0xFFB71C1C),
|
||||
iconNormal: const Color(0xFF805306),
|
||||
iconMouseOver: const Color(0xFFFFFFFF));
|
||||
|
||||
class CloseWindowButton extends WindowButton {
|
||||
CloseWindowButton(
|
||||
{Key? key,
|
||||
WindowButtonColors? colors,
|
||||
VoidCallback? onPressed,
|
||||
bool? animate})
|
||||
: super(
|
||||
key: key,
|
||||
colors: colors ?? _defaultCloseButtonColors,
|
||||
animate: animate ?? false,
|
||||
iconBuilder: (buttonContext) =>
|
||||
CloseIcon(color: buttonContext.iconColor),
|
||||
onPressed: onPressed ?? () => windowManager.close());
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:reboot_launcher/src/ui/widget/os/window_button.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/server_dialogs.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
|
||||
class ServerButton extends StatefulWidget {
|
||||
const ServerButton({Key? key}) : super(key: key);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
|
||||
|
||||
class ServerTypeSelector extends StatelessWidget {
|
||||
final ServerController _serverController = Get.find<ServerController>();
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
|
||||
|
||||
import 'package:reboot_launcher/src/util/selector.dart';
|
||||
|
||||
class FileSelector extends StatefulWidget {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
class FluentCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
const FluentCard({Key? key, required this.child}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Mica(
|
||||
elevation: 1,
|
||||
child: Card(
|
||||
backgroundColor: FluentTheme.of(context).menuColor,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)),
|
||||
child: child
|
||||
)
|
||||
);
|
||||
}
|
||||
56
lib/src/ui/widget/shared/profile_widget.dart
Normal file
56
lib/src/ui/widget/shared/profile_widget.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../controller/game_controller.dart';
|
||||
|
||||
class ProfileWidget extends StatelessWidget {
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
|
||||
ProfileWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 12.0
|
||||
),
|
||||
child: GestureDetector(
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 64,
|
||||
height: 64,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle
|
||||
),
|
||||
child: Image.asset("assets/images/user.png")
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12.0,
|
||||
),
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Auties00",
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"alautiero@gmail.com",
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w100
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:html/parser.dart' show parse;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_launcher/src/model/fortnite_build.dart';
|
||||
import 'package:reboot_launcher/src/util/time.dart';
|
||||
import 'package:reboot_launcher/src/util/version.dart' as parser;
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'os.dart';
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:reboot_launcher/src/util/server.dart';
|
||||
|
||||
import '../model/fortnite_version.dart';
|
||||
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||
|
||||
String? checkVersion(String? text, List<FortniteVersion> versions) {
|
||||
if (text == null || text.isEmpty) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
import '../ui/dialog/dialog.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
|
||||
|
||||
|
||||
String? lastError;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:win32/win32.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
final _kernel32 = DynamicLibrary.open('kernel32.dll');
|
||||
final _CreateRemoteThread = _kernel32.lookupFunction<
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:win32/win32.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'dart:ffi';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
|
||||
const int appBarSize = 2;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:win32/src/kernel32.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
final _ntdll = DynamicLibrary.open('ntdll.dll');
|
||||
|
||||
@@ -2,15 +2,14 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:ini/ini.dart';
|
||||
import 'package:process_run/shell.dart';
|
||||
import 'package:reboot_launcher/src/model/server_type.dart';
|
||||
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:shelf_proxy/shelf_proxy.dart';
|
||||
import 'package:shelf/shelf_io.dart';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shelf_proxy/shelf_proxy.dart';
|
||||
|
||||
final serverLogFile = File("${logsDirectory.path}\\server.log");
|
||||
final serverDirectory = Directory("${assetsDirectory.path}\\lawin");
|
||||
|
||||
Reference in New Issue
Block a user