Skip to content

Commit

Permalink
Add scroll position feature for WoxPreview
Browse files Browse the repository at this point in the history
This will add `tail -f` like feature in Wox preview when refreshing result
  • Loading branch information
qianlifeng committed Dec 22, 2023
1 parent eef664f commit 1a8fd5b
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 37 deletions.
75 changes: 51 additions & 24 deletions Wox.UI.Flutter/wox/lib/components/wox_preview_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,36 @@ import 'package:wox/components/wox_image_view.dart';
import 'package:wox/entity/wox_image.dart';
import 'package:wox/entity/wox_preview.dart';
import 'package:wox/entity/wox_theme.dart';
import 'package:wox/enums/wox_preview_scroll_position_enum.dart';
import 'package:wox/enums/wox_preview_type_enum.dart';
import 'package:wox/utils/log.dart';
import 'package:wox/utils/wox_http_util.dart';

class WoxPreviewView extends StatelessWidget {
class WoxPreviewView extends StatefulWidget {
final WoxPreview woxPreview;
final WoxTheme woxTheme;

const WoxPreviewView({super.key, required this.woxPreview, required this.woxTheme});

@override
State<WoxPreviewView> createState() => _WoxPreviewViewState();
}

class _WoxPreviewViewState extends State<WoxPreviewView> {
final scrollController = ScrollController();

@override
Widget build(BuildContext context) {
if (LoggerSwitch.enablePaintLog) Logger.instance.info("repaint: preview view data");

if (woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_REMOTE.code) {
if (widget.woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_REMOTE.code) {
return FutureBuilder<WoxPreview>(
future: WoxHttpUtil.instance.getData<WoxPreview>(woxPreview.previewData),
future: WoxHttpUtil.instance.getData<WoxPreview>(widget.woxPreview.previewData),
builder: (context, snapshot) {
if (snapshot.hasData) {
return WoxPreviewView(
woxPreview: snapshot.data!,
woxTheme: woxTheme,
woxTheme: widget.woxTheme,
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
Expand All @@ -37,24 +45,33 @@ class WoxPreviewView extends StatelessWidget {
);
}

if (widget.woxPreview.scrollPosition == WoxPreviewScrollPositionEnum.WOX_PREVIEW_SCROLL_POSITION_BOTTOM.code) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (scrollController.hasClients) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
}
});
}

Widget contentWidget = const SizedBox();
if (woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_MARKDOWN.code) {
if (widget.woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_MARKDOWN.code) {
var styleTheme = Theme.of(context).copyWith(
textTheme: Theme.of(context).textTheme.apply(
bodyColor: fromCssColor(woxTheme.previewFontColor),
displayColor: fromCssColor(woxTheme.previewFontColor),
bodyColor: fromCssColor(widget.woxTheme.previewFontColor),
displayColor: fromCssColor(widget.woxTheme.previewFontColor),
),
cardColor: Colors.transparent,
);
contentWidget = Markdown(
data: woxPreview.previewData,
controller: scrollController,
data: widget.woxPreview.previewData,
padding: EdgeInsets.zero,
selectable: true,
styleSheet: MarkdownStyleSheet.fromTheme(styleTheme).copyWith(
horizontalRuleDecoration: BoxDecoration(
border: Border(
top: BorderSide(
color: fromCssColor(woxTheme.previewFontColor).withOpacity(0.6),
color: fromCssColor(widget.woxTheme.previewFontColor).withOpacity(0.6),
width: 1,
),
bottom: const BorderSide(
Expand All @@ -64,12 +81,21 @@ class WoxPreviewView extends StatelessWidget {
),
),
));
} else if (woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_TEXT.code) {
contentWidget = SelectableText(woxPreview.previewData, style: TextStyle(color: fromCssColor(woxTheme.previewFontColor)));
} else if (woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_IMAGE.code) {
final parsedWoxImage = WoxImage.parse(woxPreview.previewData);
} else if (widget.woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_TEXT.code) {
contentWidget = SingleChildScrollView(
controller: scrollController,
child: SelectableText(
widget.woxPreview.previewData,
style: TextStyle(color: fromCssColor(widget.woxTheme.previewFontColor)),
),
);
} else if (widget.woxPreview.previewType == WoxPreviewTypeEnum.WOX_PREVIEW_TYPE_IMAGE.code) {
final parsedWoxImage = WoxImage.parse(widget.woxPreview.previewData);
if (parsedWoxImage == null) {
contentWidget = SelectableText("Invalid image data: ${woxPreview.previewData}", style: const TextStyle(color: Colors.red));
contentWidget = SelectableText(
"Invalid image data: ${widget.woxPreview.previewData}",
style: const TextStyle(color: Colors.red),
);
} else {
contentWidget = Center(
child: WoxImageView(woxImage: parsedWoxImage),
Expand All @@ -84,27 +110,28 @@ class WoxPreviewView extends StatelessWidget {
children: [
Expanded(
child: Theme(
data: ThemeData(
textSelectionTheme: TextSelectionThemeData(
selectionColor: fromCssColor(woxTheme.previewTextSelectionColor),
),
data: ThemeData(
textSelectionTheme: TextSelectionThemeData(
selectionColor: fromCssColor(widget.woxTheme.previewTextSelectionColor),
),
child: contentWidget),
),
child: contentWidget,
),
),
//show previewProperties
if (woxPreview.previewProperties.isNotEmpty)
if (widget.woxPreview.previewProperties.isNotEmpty)
Container(
padding: const EdgeInsets.only(top: 10.0),
child: Column(
children: [
...woxPreview.previewProperties.entries.map((e) => Column(
...widget.woxPreview.previewProperties.entries.map((e) => Column(
children: [
Divider(color: fromCssColor(woxTheme.previewSplitLineColor)),
Divider(color: fromCssColor(widget.woxTheme.previewSplitLineColor)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(e.key, style: TextStyle(color: fromCssColor(woxTheme.previewPropertyTitleColor))),
Text(e.value, style: TextStyle(color: fromCssColor(woxTheme.previewPropertyContentColor))),
Text(e.key, style: TextStyle(color: fromCssColor(widget.woxTheme.previewPropertyTitleColor))),
Text(e.value, style: TextStyle(color: fromCssColor(widget.woxTheme.previewPropertyContentColor))),
],
),
],
Expand Down
10 changes: 6 additions & 4 deletions Wox.UI.Flutter/wox/lib/entity/wox_preview.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import 'dart:convert';

import 'package:wox/enums/wox_preview_scroll_position_enum.dart';
import 'package:wox/enums/wox_preview_type_enum.dart';

class WoxPreview {
late WoxPreviewType previewType;
late String previewData;
late Map<String, String> previewProperties;
late WoxPreviewScrollPosition scrollPosition;

WoxPreview({required this.previewType, required this.previewData, required this.previewProperties});
WoxPreview({required this.previewType, required this.previewData, required this.previewProperties, required this.scrollPosition});

@override
int get hashCode => previewType.hashCode ^ previewData.hashCode ^ previewProperties.hashCode;
Expand All @@ -23,17 +23,19 @@ class WoxPreview {
previewType = json['PreviewType'];
previewData = json['PreviewData'];
previewProperties = Map<String, String>.from(json['PreviewProperties'] ?? {});
scrollPosition = json['ScrollPosition'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['PreviewType'] = previewType;
data['PreviewData'] = previewData;
data['PreviewProperties'] = previewProperties;
data['ScrollPosition'] = scrollPosition;
return data;
}

static WoxPreview empty() {
return WoxPreview(previewType: "", previewData: "", previewProperties: {});
return WoxPreview(previewType: "", previewData: "", previewProperties: {}, scrollPosition: "");
}
}
12 changes: 12 additions & 0 deletions Wox.UI.Flutter/wox/lib/enums/wox_preview_scroll_position_enum.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
typedef WoxPreviewScrollPosition = String;

enum WoxPreviewScrollPositionEnum {
WOX_PREVIEW_SCROLL_POSITION_BOTTOM("bottom", "bottom");

final String code;
final String value;

const WoxPreviewScrollPositionEnum(this.code, this.value);

static String getValue(String code) => WoxPreviewScrollPositionEnum.values.firstWhere((activity) => activity.code == code).value;
}
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 @@ -38,4 +38,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 16208599a12443d53889ba2270a4985981cfb204

COCOAPODS: 1.14.3
COCOAPODS: 1.12.1
9 changes: 5 additions & 4 deletions Wox/plugin/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,6 @@ func (m *Manager) PolishResult(ctx context.Context, pluginInstance *Instance, qu
// convert icon
result.Icon = ConvertIcon(ctx, result.Icon, pluginInstance.PluginDirectory)

// if query is input and trigger keyword is global, disable preview
if query.IsGlobalQuery() {
result.Preview = WoxPreview{}
}
// if query is selection, replace preview with selection
if query.Type == QueryTypeSelection {
if query.Selection.Type == util.SelectionTypeText {
Expand Down Expand Up @@ -409,6 +405,11 @@ func (m *Manager) PolishResult(ctx context.Context, pluginInstance *Instance, qu
}
}

// if query is input and trigger keyword is global, disable preview
if query.IsGlobalQuery() {
result.Preview = WoxPreview{}
}

// store preview for ui invoke later
// because preview may contain some heavy data (E.g. image or large text), we will store preview in cache and only send preview to ui when user select the result
if result.Preview.PreviewType != "" && result.Preview.PreviewType != WoxPreviewTypeRemote {
Expand Down
6 changes: 6 additions & 0 deletions Wox/plugin/preview.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package plugin

type WoxPreviewType = string
type WoxPreviewScrollPosition = string

const (
WoxPreviewTypeMarkdown = "markdown"
Expand All @@ -10,8 +11,13 @@ const (
WoxPreviewTypeRemote = "remote" // when type is remote, data should be url to load WoxPreview
)

const (
WoxPreviewScrollPositionBottom = "bottom" // scroll to bottom after preview first show
)

type WoxPreview struct {
PreviewType WoxPreviewType
PreviewData string
PreviewProperties map[string]string // key support i18n
ScrollPosition WoxPreviewScrollPosition
}
11 changes: 7 additions & 4 deletions Wox/plugin/system/chatgpt.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,9 @@ func (c *ChatgptPlugin) queryConversation(ctx context.Context, query plugin.Quer
chatHistory += fmt.Sprintf("You: %s\n", query.Search)
}
c.activeChatResult.Preview = plugin.WoxPreview{
PreviewType: plugin.WoxPreviewTypeMarkdown,
PreviewData: chatHistory,
PreviewType: plugin.WoxPreviewTypeMarkdown,
PreviewData: chatHistory,
ScrollPosition: plugin.WoxPreviewScrollPositionBottom,
}
c.api.Log(ctx, fmt.Sprintf("active chat refresh interval: %d", c.activeChatResult.RefreshInterval))
c.activeChatResult.Actions = []plugin.QueryResultAction{
Expand Down Expand Up @@ -198,6 +199,7 @@ func (c *ChatgptPlugin) queryConversation(ctx context.Context, query plugin.Quer
} else {
current.Preview.PreviewData += deltaAnswer
}
current.Preview.ScrollPosition = plugin.WoxPreviewScrollPositionBottom

c.activeChatAnswer += deltaAnswer
return current
Expand Down Expand Up @@ -313,8 +315,9 @@ func (c *ChatgptPlugin) queryConversation(ctx context.Context, query plugin.Quer
SubTitle: timeago.English.Format(util.ParseTimeStamp(chat.CreatedTimestamp)),
Icon: chatgptIcon,
Preview: plugin.WoxPreview{
PreviewType: plugin.WoxPreviewTypeMarkdown,
PreviewData: chatHistory,
PreviewType: plugin.WoxPreviewTypeMarkdown,
PreviewData: chatHistory,
ScrollPosition: plugin.WoxPreviewScrollPositionBottom,
},
Actions: []plugin.QueryResultAction{
{
Expand Down

0 comments on commit 1a8fd5b

Please sign in to comment.