Skip to content

Commit

Permalink
Optimize flutter UI [WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
qianlifeng committed Nov 30, 2023
1 parent 41ccc3f commit 3f9ca2d
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 76 deletions.
147 changes: 78 additions & 69 deletions Wox.UI.Flutter/wox/lib/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,54 @@ import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:logger/logger.dart';
import 'package:uuid/v4.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:window_manager/window_manager.dart';
import 'package:wox/utils/websocket.dart';

import 'entity.dart';

class WoxController extends GetxController {
final query = ChangedQuery.empty().obs;
final queryTextFieldController = TextEditingController();
final queryFocusNode = FocusNode();
final queryResults = <QueryResult>[].obs;
final currentPreview = WoxPreview.empty().obs;
final activeResultIndex = 0.obs;
final isShowActionList = false.obs;
var onQueryChangeTimer = Timer(const Duration(milliseconds: 200), () => {});
var hasLastQueryResult = false;

final isShowActionPanel = false.obs;
final isShowPreviewPanel = false.obs;
var clearQueryResultsTimer = Timer(const Duration(milliseconds: 200), () => {});
late final WoxWebsocket ws;
static const double maxHeight = 500;

late final WebSocketChannel channel;

void connect() {
channel = WebSocketChannel.connect(Uri.parse("ws://localhost:34987/ws"));
channel.stream.listen((event) {
var msg = WebsocketMsg.fromJson(jsonDecode(event));
if (msg.method == "ToggleApp") {
toggleApp(ShowAppParams.fromJson(msg.data));
} else if (msg.method == "HideApp") {
hide();
} else if (msg.method == "ShowApp") {
hide();
} else if (msg.method == "ChangeQuery") {
onQueryChanged(ChangedQuery.fromJson(msg.data));
} else if (msg.method == "Query") {
var results = <QueryResult>[];
for (var item in msg.data) {
results.add(QueryResult.fromJson(item));
}
onReceiveQueryResults(results);
@override
void onInit() {
super.onInit();
_setupWebSocket();
}

void _setupWebSocket() {
ws = WoxWebsocket(Uri.parse("ws://localhost:34987/ws"), onMessageReceived: _handleWebSocketMessage);
ws.connect();
}

void _handleWebSocketMessage(event) {
var msg = WebsocketMsg.fromJson(jsonDecode(event));
if (msg.method == "ToggleApp") {
toggleApp(ShowAppParams.fromJson(msg.data));
} else if (msg.method == "HideApp") {
hide();
} else if (msg.method == "ShowApp") {
show(ShowAppParams.fromJson(msg.data));
} else if (msg.method == "ChangeQuery") {
var changedQuery = ChangedQuery.fromJson(msg.data);
changedQuery.queryId = const UuidV4().generate();
onQueryChanged(changedQuery);
} else if (msg.method == "Query") {
var results = <QueryResult>[];
for (var item in msg.data) {
results.add(QueryResult.fromJson(item));
}
});
onReceiveQueryResults(results);
}
}

Future<void> toggleApp(ShowAppParams params) async {
Expand All @@ -56,6 +65,19 @@ class WoxController extends GetxController {
}
}

Future<void> show(ShowAppParams params) async {
if (params.selectAll) {
selectAll();
}
if (params.position.type == positionTypeMouseScreen) {
await windowManager.setPosition(Offset(params.position.x.toDouble(), params.position.y.toDouble()));
}

await windowManager.show();
await windowManager.focus();
queryFocusNode.requestFocus();
}

Future<void> hide() async {
await windowManager.blur();
await windowManager.hide();
Expand All @@ -69,6 +91,7 @@ class WoxController extends GetxController {
}

currentPreview.value = queryResults[activeResultIndex.value].preview;
isShowPreviewPanel.value = currentPreview.value.previewData != "";
queryResults.refresh();
}

Expand All @@ -80,6 +103,7 @@ class WoxController extends GetxController {
}

currentPreview.value = queryResults[activeResultIndex.value].preview;
isShowPreviewPanel.value = currentPreview.value.previewData != "";
queryResults.refresh();
}

Expand All @@ -90,47 +114,37 @@ class WoxController extends GetxController {
"resultId": result.id,
"actionId": action.id,
});
channel.sink.add(jsonEncode(msg));
ws.sendMessage(msg);

if (!action.preventHideAfterAction) {
await hide();
}
}

void toggleActionList() {
isShowActionList.value = !isShowActionList.value;
isShowActionPanel.value = !isShowActionPanel.value;
resizeHeight();
}

void resetActiveResultIndex() {
void resetActiveResult() {
activeResultIndex.value = 0;

//reset preview
if (queryResults.isNotEmpty) {
currentPreview.value = queryResults[activeResultIndex.value].preview;
} else {
currentPreview.value = WoxPreview.empty();
}
isShowPreviewPanel.value = currentPreview.value.previewData != "";
}

void selectAll() {
queryTextFieldController.selection = TextSelection(baseOffset: 0, extentOffset: queryTextFieldController.text.length);
}

Future<void> show(ShowAppParams params) async {
if (params.selectAll) {
selectAll();
}
if (params.position.type == positionTypeMouseScreen) {
await windowManager.setPosition(Offset(params.position.x.toDouble(), params.position.y.toDouble()));
}

await windowManager.show();
await windowManager.focus();
}

void onQueryChanged(ChangedQuery query) {
resetActiveResultIndex();
this.query.value = query;
isShowActionList.value = false;
isShowActionPanel.value = false;
if (query.queryType == queryTypeInput) {
queryTextFieldController.text = query.queryText;
} else {
Expand All @@ -141,17 +155,16 @@ class WoxController extends GetxController {
resizeHeight();
return;
}
hasLastQueryResult = false;
onQueryChangeTimer.cancel();

onQueryChangeTimer = Timer(
const Duration(milliseconds: 200),
// delay clear results, otherwise windows height will shrink immediately,
// and then the query result is received which will expand the windows height. so it will causes window flicker
clearQueryResultsTimer.cancel();
clearQueryResultsTimer = Timer(
const Duration(milliseconds: 100),
() {
if (!hasLastQueryResult) {
Logger().i("clear results");
queryResults.clear();
resizeHeight();
}
Logger().i("clear results");
queryResults.clear();
resizeHeight();
},
);

Expand All @@ -161,40 +174,36 @@ class WoxController extends GetxController {
"queryText": query.queryText,
"querySelection": query.querySelection.toJson(),
});
channel.sink.add(jsonEncode(msg));
ws.sendMessage(msg);
}

void onReceiveQueryResults(List<QueryResult> results) {
if (results.isEmpty) {
return;
}
//not current query result
if (query.value.queryId != results.first.queryId) {
if (results.isEmpty || query.value.queryId != results.first.queryId) {
return;
}

hasLastQueryResult = true;
//cancel clear results timer
clearQueryResultsTimer.cancel();

final finalResults = <QueryResult>[];
queryResults.where((i) => i.queryId == query.value.queryId).forEach((element) {
finalResults.remove(element);
});
finalResults.addAll(results);

//sort by score desc
//merge and sort results
final currentQueryResults = queryResults.where((item) => item.queryId == query.value.queryId).toList();
final finalResults = List<QueryResult>.from(currentQueryResults)..addAll(results);
finalResults.sort((a, b) => b.score.compareTo(a.score));

queryResults.assignAll(finalResults);

//reset active result and preview
if (currentQueryResults.isEmpty) {
resetActiveResult();
}

resizeHeight();
}

void resizeHeight() {
//based on current query result count
const queryBoxHeight = 48;
const resultItemHeight = 40;
var resultHeight = queryResults.length * resultItemHeight;
if (resultHeight > maxHeight || isShowActionList.value) {
if (resultHeight > maxHeight || isShowActionPanel.value || isShowPreviewPanel.value) {
resultHeight = maxHeight.toInt();
}
final totalHeight = queryBoxHeight + resultHeight;
Expand Down
5 changes: 2 additions & 3 deletions Wox.UI.Flutter/wox/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ void main() async {
dark: false,
);

final woxController = WoxController();
woxController.connect();
Get.lazyPut(() => woxController);
Get.put(WoxController());

WindowOptions windowOptions = const WindowOptions(
size: Size(800, 300),
Expand All @@ -34,6 +32,7 @@ void main() async {
await windowManager.setVisibleOnAllWorkspaces(true, visibleOnFullScreen: true);
}
await windowManager.setAsFrameless();
await windowManager.setResizable(false);
await windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
Expand Down
39 changes: 39 additions & 0 deletions Wox.UI.Flutter/wox/lib/utils/websocket.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'dart:convert';

import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:wox/entity.dart';

class WoxWebsocket {
WebSocketChannel? channel;
final Uri uri;

Function onMessageReceived;

WoxWebsocket(this.uri, {required this.onMessageReceived});

void connect() {
channel?.sink.close();
channel = null;

channel = WebSocketChannel.connect(uri);
channel!.stream.listen(
(event) {
onMessageReceived(event);
},
onDone: () {
_reconnect();
},
);
}

void sendMessage(WebsocketMsg msg) {
channel?.sink.add(jsonEncode(msg));
}

void _reconnect() {
Future.delayed(const Duration(seconds: 1), () {
print("Attempting to reconnect to WebSocket...");
connect();
});
}
}
7 changes: 4 additions & 3 deletions Wox.UI.Flutter/wox/lib/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class WoxView extends GetView<WoxController> {
}),
child: TextField(
autofocus: true,
focusNode: controller.queryFocusNode,
controller: controller.queryTextFieldController,
onChanged: (value) {
final query = ChangedQuery(queryId: const UuidV4().generate(), queryType: queryTypeInput, queryText: value, querySelection: Selection.empty());
Expand All @@ -63,7 +64,7 @@ class WoxView extends GetView<WoxController> {
return ConstrainedBox(
constraints: const BoxConstraints(maxHeight: WoxController.maxHeight),
child: Stack(
fit: controller.isShowActionList.value ? StackFit.expand : StackFit.loose,
fit: (controller.isShowActionPanel.value || controller.isShowPreviewPanel.value) ? StackFit.expand : StackFit.loose,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
Expand All @@ -79,10 +80,10 @@ class WoxView extends GetView<WoxController> {
},
),
),
if (controller.currentPreview.value.previewData != "") Expanded(child: WoxPreviewView(woxPreview: controller.currentPreview.value)),
if (controller.isShowPreviewPanel.value) Expanded(child: WoxPreviewView(woxPreview: controller.currentPreview.value)),
],
),
if (controller.isShowActionList.value) const Positioned(right: 10, bottom: 10, child: SizedBox(width: 100, height: 48, child: Text("Action List")))
if (controller.isShowActionPanel.value) const Positioned(right: 10, bottom: 10, child: SizedBox(width: 100, height: 48, child: Text("Action List")))
],
),
);
Expand Down
2 changes: 1 addition & 1 deletion Wox.UI.Flutter/wox/macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 16208599a12443d53889ba2270a4985981cfb204

COCOAPODS: 1.11.3
COCOAPODS: 1.12.1

0 comments on commit 3f9ca2d

Please sign in to comment.