评论

一个跨平台的ChatGPT悬浮窗工具

一个跨平台的 ChatGPT 悬浮窗工具

使用 avalonia 实现的 ChatGPT 的工具,设计成悬浮窗,并且支持插件。

如何实现悬浮窗?

在使用 avalonia 实现悬浮窗也是非常的简单的。

实现我们需要将窗体设置成无边框

在 Window 根节点添加一下属性,想要在Linux下生效请务必添加 SystemDecorations 属性

ExtendClientAreaToDecorationsHint="True"

ExtendClientAreaChromeHints="NoChrome"

ExtendClientAreaTitleBarHeightHint="-1"

SystemDecorations="None"

这样我们的窗口就设置成了无边框。

然后我们还需要将窗体的大小固定,

Height= "50"

MaxHeight= "50"

Width= "{Binding Width}"

MaxWidth= "{Binding Width}"

高度固定,宽度绑定到 ViewModel 的 Width 属性中,默认 270 ,

接下来给出所有代码,

< Windowxmlns= "https://github.com/avaloniaui"

xmlns:x= "http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:vm= "using:Gotrays.Suspension.Client.ViewModels"

xmlns:d= "http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc= "http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:valueConverter= "clr-namespace:Gotrays.Suspension.Client.ValueConverter"

xmlns:md= "clr-namespace:Markdown.Avalonia;assembly=Markdown.Avalonia"

xmlns:avedit= "https://github.com/avaloniaui/avaloniaedit"

xmlns:ctxt= "clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia"

mc:Ignorable= "d"d:DesignWidth= "800"d:DesignHeight= "450"

x:Class= "Gotrays.Suspension.Client.Views.MainWindow"

x:DataType= "vm:MainWindowViewModel"

ExtendClientAreaToDecorationsHint= "True"

ExtendClientAreaChromeHints= "NoChrome"

ExtendClientAreaTitleBarHeightHint= "-1"

SystemDecorations= "None"

WindowStartupLocation= "CenterScreen"

Height= "50"

MaxHeight= "50"

Width= "{Binding Width}"

MaxWidth= "{Binding Width}"

Icon= "/Assets/ai.png"

Title= "Gotrays.Suspension.Client">

< Window.Resources>

< valueConverter:ImageConverterx:Key= "ImageConverter"/>

< valueConverter:ChatToStyleConverterx:Key= "ChatToStyleConverter"/>

</ Window.Resources>

< Design.DataContext>

< vm:MainWindowViewModel/>

</ Design.DataContext>

< Window.Styles>

< StyleSelector= "Window">

< SetterProperty= "BorderThickness"Value= "0"/>

< SetterProperty= "Padding"Value= "0"/>

< SetterProperty= "Background"Value= "Transparent"/>

< SetterProperty= "BorderBrush"Value= "Transparent"/>

</ Style>

< StyleSelector= "TextBox.red:pointerover">

< SetterProperty= "Opacity"Value= "1"/>

</ Style>

</ Window.Styles>

< BorderName= "MainBorder"CornerRadius= "1000"Background= "Black"Margin= "0,0,0,0"Padding= "0,0,0,0"

HorizontalAlignment= "Left"VerticalAlignment= "Center"Width= "100"Height= "50">

< Grid>

<!-- 图标 -->

< ImageSource= "../Assets/ai.png"Name= "Logo"HorizontalAlignment= "Left"VerticalAlignment= "Center"

Width= "46"

Tapped= "Logo_OnTapped"

RenderOptions.BitmapInterpolationMode= "HighQuality"

PointerPressed= "OnLogoClick"

PointerEntered= "Logo_OnPointerEntered"

PointerExited= "Logo_OnPointerExited"

Height= "46"Margin= "0,0,0,0"/>

<!-- 模型选择 -->

< PopupName= "ModulePopup"IsOpen= "False"PlacementTarget= "{Binding ElementName=MainBorder}"

PlacementMode= "Top">

< StackPanelMargin= "5">

< BorderBackground= "#1F1F1F"BorderBrush= "Black"BorderThickness= "1"CornerRadius= "12"

MaxHeight= "400"Width= "120">

< ScrollViewerName= "ModuleScrollViewer"VerticalScrollBarVisibility= "Auto">

< ItemsControlCornerRadius= "12"ItemsSource= "{Binding Modules}"Margin= "2">

< ItemsControl.ItemTemplate>

< DataTemplate>

< BorderMargin= "5"

Background= "{Binding Color}"

PointerExited= "OnSelectStackPointerExited"

PointerEntered= "OnSelectStackPointerEntered"

PointerPressed= "OnSelectStackPointerPressed"

Tag= "{Binding GetThis}"

CornerRadius= "8">

<!-- 左边显示图标,右边显示名称 -->

< StackPanelOrientation= "Horizontal">

< Image

RenderOptions.BitmapInterpolationMode= "HighQuality"

Source= "{Binding Icon, Converter={StaticResource ImageConverter}}"

HorizontalAlignment= "Left"

Width= "20"

Height= "20"/>

< TextBlockTextWrapping= "Wrap"Width= "60"Text= "{Binding Title}"

Margin= "5"Foreground= "White"/>

</ StackPanel>

</ Border>

</ DataTemplate>

</ ItemsControl.ItemTemplate>

</ ItemsControl>

</ ScrollViewer>

</ Border>

</ StackPanel>

</ Popup>

<!-- 静止状态下的搜索按钮 -->

< BorderPointerPressed= "SearchBorder_OnPointerPressed"

PointerEntered= "searchBorder_PointerEnter"

PointerExited= "OnPointerExited"

Name= "searchBorder"

CornerRadius= "1000"Background= "#000000"BorderBrush= "#FFFFFF"

BorderThickness= "1"Margin= "50,0,0,0"Padding= "0,0,0,0"HorizontalAlignment= "Left"

VerticalAlignment= "Center"Width= "46"Height= "46"Cursor= "Hand">

< ImageSource= "../Assets/search.png"

RenderOptions.BitmapInterpolationMode= "HighQuality"

HorizontalAlignment= "Center"VerticalAlignment= "Center"

Width= "20"Height= "20"Margin= "0,0,0,0"/>

</ Border>

<!-- 当点击搜索按钮时,显示搜索框 -->

< TextBoxFontSize= "20"Name= "SearchText"Margin= "50,0,0,0"IsVisible= "False"Width= "0"Height= "40"

HorizontalAlignment= "Left"VerticalAlignment= "Center">

< TextBox.Styles>

< Styles>

< StyleSelector= "TextBox">

< SetterProperty= "CornerRadius"Value= "0,50,50,0"> </ Setter>

</ Style>

</ Styles>

</ TextBox.Styles>

< TextBox.Transitions>

< Transitions>

< DoubleTransitionProperty= "Width"Duration= "0:0:0.1"/>

</ Transitions>

</ TextBox.Transitions>

</ TextBox>

<!-- 消息显示区域 -->

< Popupx:Name= "MessagePopup"

IsOpen= "False"

PlacementTarget= "{Binding ElementName=MainBorder}"

PlacementMode= "Bottom">

< StackPanel

PointerEntered= "MessagePopup_OnPointerEntered"

PointerExited= "MessagePopup_OnPointerExited"Margin= "5">

< BorderName= "MessageBorder"

Background= "#1F1F1F"

BorderBrush= "Black"

BorderThickness= "1"

CornerRadius= "12"

MaxHeight= "300">

< ScrollViewerName= "ScrollViewer"VerticalScrollBarVisibility= "Auto">

< ItemsControlItemsSource= "{Binding Messages}"CornerRadius= "12"Margin= "2">

< ItemsControl.ItemTemplate>

< DataTemplate>

< StackPanelMargin= "5">

< StackPanel.Resources>

< valueConverter:ChatToBackgroundConverter

x:Key= "ChatToBackgroundConverter"/>

</ StackPanel.Resources>

< Border

Background= "{Binding Chat, Converter={StaticResource ChatToBackgroundConverter}}"

CornerRadius= "5">

< md:MarkdownScrollViewer

VerticalAlignment= "Stretch"

MarkdownStyleName= "Standard"

SaveScrollValueWhenContentUpdated= "True"

Markdown= "{Binding Message}">

< md:MarkdownScrollViewer.Styles>

< StyleSelector= "ctxt|CCode">

< Style.Setters>

< SetterProperty= "BorderBrush"Value= "Green"/>

< SetterProperty= "BorderThickness"Value= "2"/>

< SetterProperty= "Padding"Value= "2"/>

< SetterProperty= "MonospaceFontFamily"Value= "Meiryo"/>

< SetterProperty= "Foreground"Value= "DarkGreen"/>

< SetterProperty= "Background"Value= "LightGreen"/>

</ Style.Setters>

</ Style>

< StyleSelector= "Border.CodeBlock">

< Style.Setters>

< SetterProperty= "BorderBrush"Value= "Blue"/>

< SetterProperty= "BorderThickness"Value= "0,5,0,5"/>

< SetterProperty= "Margin"Value= "5,0,5,0"/>

< SetterProperty= "Background"Value= "LightBlue"/>

</ Style.Setters>

</ Style>

< StyleSelector= "TextBlock.CodeBlock">

< Style.Setters>

< SetterProperty= "Foreground"Value= "DarkBlue"/>

< SetterProperty= "FontFamily"Value= "Meiryo"/>

</ Style.Setters>

</ Style>

< StyleSelector= "avedit|TextEditor">

< SetterProperty= "Background"Value= "Gray"/>

< SetterProperty= "CornerRadius"Value= "10"> </ Setter>

</ Style>

</ md:MarkdownScrollViewer.Styles>

</ md:MarkdownScrollViewer>

</ Border>

</ StackPanel>

</ DataTemplate>

</ ItemsControl.ItemTemplate>

</ ItemsControl>

</ ScrollViewer>

</ Border>

</ StackPanel>

</ Popup>

</ Grid>

< Border.Transitions>

< Transitions>

< DoubleTransitionProperty= "Width"Duration= "0:0:0.2"/>

</ Transitions>

</ Border.Transitions>

</ Border>

</ Window>

只需要设置无边框并且固定大小。悬浮窗的效果就达到了。

我们看看执行效果

image-20230702133719931

就这样简单的悬浮窗写好了,我们使用一下悬浮窗的搜索功能

image-20230702133757221

这个就是简单的使用效果,对比其他的工具,这个悬浮窗更简洁,并且跨平台和开源。

image-20230702133839454

目前的项目结构。

plugin 下面的项目是默认的插件,用于搜索系统文件(未完善)

Gotrays.Suspension.Client 则是实际的客户端。

Gotrays.Suspension.PlugIn 则是插件定义的接口规范。

Gotrays.Update 则是检查更新程序,用于更新主程序。

实现插件 plug-in

插件模块,用于扩展功能。

插件开发 1. 创建插件项目

在解决方案中创建一个类库项目,项目名称以 Gotrays.Suspension.PlugIn. 开头,例如 Gotrays.Suspension.PlugIn.Test 。 然后在项目中依赖 Gotrays.Suspension.PlugIn 类库。

2. 创建插件类

在项目中创建一个类,继承 Gotrays.Suspension.PlugIn.PlugInBase 类,例如:

usingGotrays.Suspension.PlugIn;

publicclassSystemTools: PlugInBase

{

publicSystemTools( )

{

Name = "系统搜索";

// 获取system.png嵌入资源的Stream

varstream = GetType.Assembly.GetManifestResourceStream( "SystemTools.system.png");

if(stream == null) return;

// 读取Stream到byte数组

varbytes = newbyte[stream.Length];

varread = stream.Read(bytes, 0, bytes.Length);

Icon = bytes;

}

// 搜索触发

publicoverrideasyncTask SearchAsync( stringvalue)

{

// 打开系统搜索

Process.Start( "explorer.exe", "search://"+ value);

awaitTask.CompletedTask;

}

protectedoverrideasyncTask InitAsync( IServiceCollection services) {

// 插件首次加载时执行

}

publicoverrideasyncTask BuilderServiceAsync( IServiceProvider provider)

{

// 这里可以得到服务提供者,可以通过服务提供者获取其他服务

}

protectedoverridevoidSelection( )

{

// 当插件被选中时执行

}

protectedoverridevoidUnSelection( )

{

// 当插件被取消选中时执行

}

protectedoverrideasyncTask UnloadAsync( )

{

// 当插件被卸载插件发生

}

}

工具服务会进行自动发现,无需手动注册。 只需要将程序集放置在 ./plug-in 目录下即可。 服务会在一个程序集中发现所有的插件类,并且进行注册。

按照上面的方式非常的简单就集成了插件。

开源地址

Gitee:https://gitee.com/gotrays/gotrays-suspension返回搜狐,查看更多

责任编辑:

平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
阅读 ()
大家都在看
推荐阅读