mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
fix conflicts
This commit is contained in:
@@ -112,18 +112,21 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
required this.border2,
|
||||
required this.highlight,
|
||||
required this.drag_indicator,
|
||||
required this.shadow,
|
||||
});
|
||||
|
||||
final Color? border;
|
||||
final Color? border2;
|
||||
final Color? highlight;
|
||||
final Color? drag_indicator;
|
||||
final Color? shadow;
|
||||
|
||||
static final light = ColorThemeExtension(
|
||||
border: Color(0xFFCCCCCC),
|
||||
border2: Color(0xFFBBBBBB),
|
||||
highlight: Color(0xFFE5E5E5),
|
||||
drag_indicator: Colors.grey[800],
|
||||
shadow: Colors.black,
|
||||
);
|
||||
|
||||
static final dark = ColorThemeExtension(
|
||||
@@ -131,19 +134,24 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
border2: Color(0xFFE5E5E5),
|
||||
highlight: Color(0xFF3F3F3F),
|
||||
drag_indicator: Colors.grey,
|
||||
shadow: Colors.grey,
|
||||
);
|
||||
|
||||
@override
|
||||
ThemeExtension<ColorThemeExtension> copyWith(
|
||||
{Color? border,
|
||||
Color? border2,
|
||||
Color? highlight,
|
||||
Color? drag_indicator}) {
|
||||
ThemeExtension<ColorThemeExtension> copyWith({
|
||||
Color? border,
|
||||
Color? border2,
|
||||
Color? highlight,
|
||||
Color? drag_indicator,
|
||||
Color? shadow,
|
||||
}) {
|
||||
return ColorThemeExtension(
|
||||
border: border ?? this.border,
|
||||
border2: border2 ?? this.border2,
|
||||
highlight: highlight ?? this.highlight,
|
||||
drag_indicator: drag_indicator ?? this.drag_indicator);
|
||||
border: border ?? this.border,
|
||||
border2: border2 ?? this.border2,
|
||||
highlight: highlight ?? this.highlight,
|
||||
drag_indicator: drag_indicator ?? this.drag_indicator,
|
||||
shadow: shadow ?? this.shadow,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -157,6 +165,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
border2: Color.lerp(border2, other.border2, t),
|
||||
highlight: Color.lerp(highlight, other.highlight, t),
|
||||
drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
|
||||
shadow: Color.lerp(shadow, other.shadow, t),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,6 +372,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
offstage: _dragging.isTrue,
|
||||
child: Material(
|
||||
elevation: _MenubarTheme.elevation,
|
||||
shadowColor: MyTheme.color(context).shadow,
|
||||
child: _DraggableShowHide(
|
||||
dragging: _dragging,
|
||||
fractionX: _fractionX,
|
||||
@@ -421,6 +422,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
children: [
|
||||
Material(
|
||||
elevation: _MenubarTheme.elevation,
|
||||
shadowColor: MyTheme.color(context).shadow,
|
||||
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
||||
color: Theme.of(context)
|
||||
.menuBarTheme
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/utils/event_loop.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
@@ -45,6 +45,7 @@ class FileModel {
|
||||
|
||||
late final GetSessionID getSessionID;
|
||||
String get sessionID => getSessionID();
|
||||
late final FileDialogEventLoop evtLoop;
|
||||
|
||||
FileModel(this.parent) {
|
||||
getSessionID = () => parent.target?.id ?? "";
|
||||
@@ -64,14 +65,17 @@ class FileModel {
|
||||
jobController: jobController,
|
||||
fileFetcher: fileFetcher,
|
||||
getOtherSideDirectoryData: () => localController.directoryData());
|
||||
evtLoop = FileDialogEventLoop();
|
||||
}
|
||||
|
||||
Future<void> onReady() async {
|
||||
await evtLoop.onReady();
|
||||
await localController.onReady();
|
||||
await remoteController.onReady();
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
await evtLoop.close();
|
||||
parent.target?.dialogManager.dismissAll();
|
||||
await localController.close();
|
||||
await remoteController.close();
|
||||
@@ -90,14 +94,26 @@ class FileModel {
|
||||
fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
||||
}
|
||||
|
||||
void overrideFileConfirm(Map<String, dynamic> evt) async {
|
||||
final resp = await showFileConfirmDialog(
|
||||
translate("Overwrite"), "${evt['read_path']}", true);
|
||||
Future<void> postOverrideFileConfirm(Map<String, dynamic> evt) async {
|
||||
evtLoop.pushEvent(
|
||||
_FileDialogEvent(WeakReference(this), FileDialogType.overwrite, evt));
|
||||
}
|
||||
|
||||
Future<void> overrideFileConfirm(Map<String, dynamic> evt,
|
||||
{bool? overrideConfirm, bool skip = false}) async {
|
||||
// If `skip == true`, it means to skip this file without showing dialog.
|
||||
// Because `resp` may be null after the user operation or the last remembered operation,
|
||||
// and we should distinguish them.
|
||||
final resp = overrideConfirm ??
|
||||
(!skip
|
||||
? await showFileConfirmDialog(translate("Overwrite"),
|
||||
"${evt['read_path']}", true, evt['is_identical'] == "true")
|
||||
: null);
|
||||
final id = int.tryParse(evt['id']) ?? 0;
|
||||
if (false == resp) {
|
||||
final jobIndex = jobController.getJob(id);
|
||||
if (jobIndex != -1) {
|
||||
jobController.cancelJob(id);
|
||||
await jobController.cancelJob(id);
|
||||
final job = jobController.jobTable[jobIndex];
|
||||
job.state = JobState.done;
|
||||
jobController.jobTable.refresh();
|
||||
@@ -111,7 +127,11 @@ class FileModel {
|
||||
// overwrite
|
||||
need_override = true;
|
||||
}
|
||||
bind.sessionSetConfirmOverrideFile(
|
||||
// Update the loop config.
|
||||
if (fileConfirmCheckboxRemember) {
|
||||
evtLoop.setSkip(!need_override);
|
||||
}
|
||||
await bind.sessionSetConfirmOverrideFile(
|
||||
id: sessionID,
|
||||
actId: id,
|
||||
fileNum: int.parse(evt['file_num']),
|
||||
@@ -119,12 +139,16 @@ class FileModel {
|
||||
remember: fileConfirmCheckboxRemember,
|
||||
isUpload: evt['is_upload'] == "true");
|
||||
}
|
||||
// Update the loop config.
|
||||
if (fileConfirmCheckboxRemember) {
|
||||
evtLoop.setOverrideConfirm(resp);
|
||||
}
|
||||
}
|
||||
|
||||
bool fileConfirmCheckboxRemember = false;
|
||||
|
||||
Future<bool?> showFileConfirmDialog(
|
||||
String title, String content, bool showCheckbox) async {
|
||||
String title, String content, bool showCheckbox, bool isIdentical) async {
|
||||
fileConfirmCheckboxRemember = false;
|
||||
return await parent.target?.dialogManager.show<bool?>(
|
||||
(setState, Function(bool? v) close) {
|
||||
@@ -149,6 +173,17 @@ class FileModel {
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 5),
|
||||
Text(content),
|
||||
Offstage(
|
||||
offstage: !isIdentical,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Text(translate("identical_file_tip"),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500))
|
||||
],
|
||||
),
|
||||
),
|
||||
showCheckbox
|
||||
? CheckboxListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
@@ -377,12 +412,12 @@ class FileController {
|
||||
}
|
||||
job.totalSize = totalSize;
|
||||
job.fileCount = fileCount;
|
||||
debugPrint("update receive details:${fd.path}");
|
||||
debugPrint("update receive details: ${fd.path}");
|
||||
jobController.jobTable.refresh();
|
||||
}
|
||||
} else if (options.value.home.isEmpty) {
|
||||
options.value.home = fd.path;
|
||||
debugPrint("init remote home:${fd.path}");
|
||||
debugPrint("init remote home: ${fd.path}");
|
||||
directory.value = fd;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -414,7 +449,7 @@ class FileController {
|
||||
includeHidden: showHidden,
|
||||
isRemote: isRemoteToLocal);
|
||||
debugPrint(
|
||||
"path:${from.path}, toPath:$toPath, to:${PathUtil.join(toPath, from.name, isWindows)}");
|
||||
"path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,7 +674,7 @@ class JobController {
|
||||
jobTable.refresh();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Failed to tryUpdateJobProgress,evt:${evt.toString()}");
|
||||
debugPrint("Failed to tryUpdateJobProgress, evt: ${evt.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,8 +712,8 @@ class JobController {
|
||||
debugPrint("jobError $evt");
|
||||
}
|
||||
|
||||
void cancelJob(int id) async {
|
||||
bind.sessionCancelJob(id: sessionID, actId: id);
|
||||
Future<void> cancelJob(int id) async {
|
||||
await bind.sessionCancelJob(id: sessionID, actId: id);
|
||||
}
|
||||
|
||||
void loadLastJob(Map<String, dynamic> evt) {
|
||||
@@ -806,7 +841,7 @@ class FileFetcher {
|
||||
Timer(Duration(seconds: 2), () {
|
||||
tasks.remove(path);
|
||||
if (c.isCompleted) return;
|
||||
c.completeError("Failed to read dir,timeout");
|
||||
c.completeError("Failed to read dir, timeout");
|
||||
});
|
||||
return c.future;
|
||||
}
|
||||
@@ -822,7 +857,7 @@ class FileFetcher {
|
||||
Timer(Duration(seconds: 2), () {
|
||||
tasks.remove(actID);
|
||||
if (c.isCompleted) return;
|
||||
c.completeError("Failed to read dir,timeout");
|
||||
c.completeError("Failed to read dir, timeout");
|
||||
});
|
||||
return c.future;
|
||||
}
|
||||
@@ -846,7 +881,7 @@ class FileFetcher {
|
||||
completer?.complete(fd);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("tryCompleteJob err :$e");
|
||||
debugPrint("tryCompleteJob err: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,3 +1202,74 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/// Define a general queue which can accepts different dialog type.
|
||||
///
|
||||
/// [Visibility]
|
||||
/// The `_FileDialogType` and `_DialogEvent` are invisible for other models.
|
||||
enum FileDialogType { overwrite, unknown }
|
||||
|
||||
class _FileDialogEvent extends BaseEvent<FileDialogType, Map<String, dynamic>> {
|
||||
WeakReference<FileModel> fileModel;
|
||||
bool? _overrideConfirm;
|
||||
bool _skip = false;
|
||||
|
||||
_FileDialogEvent(this.fileModel, super.type, super.data);
|
||||
|
||||
void setOverrideConfirm(bool? confirm) {
|
||||
_overrideConfirm = confirm;
|
||||
}
|
||||
|
||||
void setSkip(bool skip) {
|
||||
_skip = skip;
|
||||
}
|
||||
|
||||
@override
|
||||
EventCallback<Map<String, dynamic>>? findCallback(FileDialogType type) {
|
||||
final model = fileModel.target;
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
switch (type) {
|
||||
case FileDialogType.overwrite:
|
||||
return (data) async {
|
||||
return await model.overrideFileConfirm(data,
|
||||
overrideConfirm: _overrideConfirm, skip: _skip);
|
||||
};
|
||||
default:
|
||||
debugPrint("Unknown event type: $type with $data");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FileDialogEventLoop
|
||||
extends BaseEventLoop<FileDialogType, Map<String, dynamic>> {
|
||||
bool? _overrideConfirm;
|
||||
bool _skip = false;
|
||||
|
||||
@override
|
||||
Future<void> onPreConsume(
|
||||
BaseEvent<FileDialogType, Map<String, dynamic>> evt) async {
|
||||
var event = evt as _FileDialogEvent;
|
||||
event.setOverrideConfirm(_overrideConfirm);
|
||||
event.setSkip(_skip);
|
||||
debugPrint(
|
||||
"FileDialogEventLoop: consuming<jobId: ${evt.data['id']} overrideConfirm: $_overrideConfirm, skip: $_skip>");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onEventsClear() {
|
||||
_overrideConfirm = null;
|
||||
_skip = false;
|
||||
return super.onEventsClear();
|
||||
}
|
||||
|
||||
void setOverrideConfirm(bool? confirm) {
|
||||
_overrideConfirm = confirm;
|
||||
}
|
||||
|
||||
void setSkip(bool skip) {
|
||||
_skip = skip;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ class FfiModel with ChangeNotifier {
|
||||
} else if (name == 'job_error') {
|
||||
parent.target?.fileModel.jobController.jobError(evt);
|
||||
} else if (name == 'override_file_confirm') {
|
||||
parent.target?.fileModel.overrideFileConfirm(evt);
|
||||
parent.target?.fileModel.postOverrideFileConfirm(evt);
|
||||
} else if (name == 'load_last_job') {
|
||||
parent.target?.fileModel.jobController.loadLastJob(evt);
|
||||
} else if (name == 'update_folder_files') {
|
||||
|
||||
79
flutter/lib/utils/event_loop.dart
Normal file
79
flutter/lib/utils/event_loop.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
typedef EventCallback<Data> = Future<dynamic> Function(Data data);
|
||||
|
||||
abstract class BaseEvent<EventType, Data> {
|
||||
EventType type;
|
||||
Data data;
|
||||
|
||||
/// Constructor.
|
||||
BaseEvent(this.type, this.data);
|
||||
|
||||
/// Consume this event.
|
||||
@visibleForTesting
|
||||
Future<dynamic> consume() async {
|
||||
final cb = findCallback(type);
|
||||
if (cb == null) {
|
||||
return null;
|
||||
} else {
|
||||
return cb(data);
|
||||
}
|
||||
}
|
||||
|
||||
EventCallback<Data>? findCallback(EventType type);
|
||||
}
|
||||
|
||||
abstract class BaseEventLoop<EventType, Data> {
|
||||
final List<BaseEvent<EventType, Data>> _evts = [];
|
||||
Timer? _timer;
|
||||
|
||||
List<BaseEvent<EventType, Data>> get evts => _evts;
|
||||
|
||||
Future<void> onReady() async {
|
||||
// Poll every 100ms.
|
||||
_timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
|
||||
}
|
||||
|
||||
/// An Event is about to be consumed.
|
||||
Future<void> onPreConsume(BaseEvent<EventType, Data> evt) async {}
|
||||
/// An Event was consumed.
|
||||
Future<void> onPostConsume(BaseEvent<EventType, Data> evt) async {}
|
||||
/// Events are all handled and cleared.
|
||||
Future<void> onEventsClear() async {}
|
||||
/// Events start to consume.
|
||||
Future<void> onEventsStartConsuming() async {}
|
||||
|
||||
Future<void> _handleTimer(Timer timer) async {
|
||||
if (_evts.isEmpty) {
|
||||
return;
|
||||
}
|
||||
timer.cancel();
|
||||
_timer = null;
|
||||
// Handle the logic.
|
||||
await onEventsStartConsuming();
|
||||
while (_evts.isNotEmpty) {
|
||||
final evt = _evts.first;
|
||||
_evts.remove(evt);
|
||||
await onPreConsume(evt);
|
||||
await evt.consume();
|
||||
await onPostConsume(evt);
|
||||
}
|
||||
await onEventsClear();
|
||||
// Now events are all processed.
|
||||
_timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
_timer?.cancel();
|
||||
}
|
||||
|
||||
void pushEvent(BaseEvent<EventType, Data> evt) {
|
||||
_evts.add(evt);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_evts.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user