一个跨平台的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,
接下来给出所有代码,- <Window xmlns="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:
- 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:ImageConverter x:Key="ImageConverter" />
- <valueConverter:ChatToStyleConverter x:Key="ChatToStyleConverter" />
- </Window.Resources>
- <Design.DataContext>
- <vm:MainWindowViewModel />
- </Design.DataContext>
- <Window.Styles>
-
-
- </Window.Styles>
- <Border Name="MainBorder" CornerRadius="1000" Background="Black" Margin="0,0,0,0" Padding="0,0,0,0"
- HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Height="50">
- <Grid>
-
- <Image Source="../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" />
-
- <Popup Name="ModulePopup" IsOpen="False" PlacementTarget="{Binding ElementName=MainBorder}"
- PlacementMode="Top">
- <StackPanel Margin="5">
- <Border Background="#1F1F1F" BorderBrush="Black" BorderThickness="1" CornerRadius="12"
- MaxHeight="400" Width="120">
- <ScrollViewer Name="ModuleScrollViewer" VerticalScrollBarVisibility="Auto">
- <ItemsControl CornerRadius="12" ItemsSource="{Binding Modules}" Margin="2">
- <ItemsControl.ItemTemplate>
- <DataTemplate>
- <Border Margin="5"
- Background="{Binding Color}"
- PointerExited="OnSelectStackPointerExited"
- PointerEntered="OnSelectStackPointerEntered"
- PointerPressed="OnSelectStackPointerPressed"
- Tag="{Binding GetThis}"
- CornerRadius="8">
-
- <StackPanel Orientation="Horizontal">
- <Image
- RenderOptions.BitmapInterpolationMode="HighQuality"
- Source="{Binding Icon, Converter={StaticResource ImageConverter}}"
- HorizontalAlignment="Left"
- Width="20"
- Height="20" />
- <TextBlock TextWrapping="Wrap" Width="60" Text="{Binding Title}"
- Margin="5" Foreground="White" />
- </StackPanel>
- </Border>
- </DataTemplate>
- </ItemsControl.ItemTemplate>
- </ItemsControl>
- </ScrollViewer>
- </Border>
- </StackPanel>
- </Popup>
-
- <Border PointerPressed="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">
- <Image Source="../Assets/search.png"
- RenderOptions.BitmapInterpolationMode="HighQuality"
- HorizontalAlignment="Center" VerticalAlignment="Center"
- Width="20" Height="20" Margin="0,0,0,0" />
- </Border>
-
- <TextBox FontSize="20" Name="SearchText" Margin="50,0,0,0" IsVisible="False" Width="0" Height="40"
- HorizontalAlignment="Left" VerticalAlignment="Center">
- <TextBox.Styles>
-
- </Styles>
- </TextBox.Styles>
- <TextBox.Transitions>
- <Transitions>
- <DoubleTransition Property="Width" Duration="0:0:0.1" />
- </Transitions>
- </TextBox.Transitions>
- </TextBox>
-
- <Popup x:Name="MessagePopup"
- IsOpen="False"
- PlacementTarget="{Binding ElementName=MainBorder}"
- PlacementMode="Bottom">
- <StackPanel
- PointerEntered="MessagePopup_OnPointerEntered"
- PointerExited="MessagePopup_OnPointerExited" Margin="5">
- <Border Name="MessageBorder"
- Background="#1F1F1F"
- BorderBrush="Black"
- BorderThickness="1"
- CornerRadius="12"
- MaxHeight="300">
- <ScrollViewer Name="ScrollViewer" VerticalScrollBarVisibility="Auto">
- <ItemsControl ItemsSource="{Binding Messages}" CornerRadius="12" Margin="2">
- <ItemsControl.ItemTemplate>
- <DataTemplate>
- <StackPanel Margin="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>
-
-
-
-
- </md:MarkdownScrollViewer.Styles>
- </md:MarkdownScrollViewer>
- </Border>
- </StackPanel>
- </DataTemplate>
- </ItemsControl.ItemTemplate>
- </ItemsControl>
- </ScrollViewer>
- </Border>
- </StackPanel>
- </Popup>
- </Grid>
- <Border.Transitions>
- <Transitions>
- <DoubleTransition Property="Width" Duration="0:0:0.2" />
- </Transitions>
- </Border.Transitions>
- </Border>
- </Window>
复制代码 只需要设置无边框并且固定大小。悬浮窗的效果就达到了。
我们看看执行效果
<img alt="image-20230702133719931" loading="lazy">
就这样简单的悬浮窗写好了,我们使用一下悬浮窗的搜索功能
<img alt="image-20230702133757221" loading="lazy">
这个就是简单的使用效果,对比其他的工具,这个悬浮窗更简洁,并且跨平台和开源。
<img alt="image-20230702133839454" loading="lazy">
目前的项目结构。
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类,例如:- using Gotrays.Suspension.PlugIn;
- public class SystemTools : PlugInBase
- {
- public SystemTools()
- {
- Name = "系统搜索";
- // 获取system.png嵌入资源的Stream
- var stream = GetType().Assembly.GetManifestResourceStream("SystemTools.system.png");
- if (stream == null) return;
- // 读取Stream到byte数组
- var bytes = new byte[stream.Length];
- var read = stream.Read(bytes, 0, bytes.Length);
- Icon = bytes;
- }
- // 搜索触发
- public override async Task SearchAsync(string value)
- {
- // 打开系统搜索
- Process.Start("explorer.exe", "search://" + value);
- await Task.CompletedTask;
- }
-
- protected override async Task InitAsync(IServiceCollection services){
- // 插件首次加载时执行
- }
- public override async Task BuilderServiceAsync(IServiceProvider provider)
- {
- // 这里可以得到服务提供者,可以通过服务提供者获取其他服务
- }
- protected override void Selection()
- {
- // 当插件被选中时执行
- }
-
- protected override void UnSelection()
- {
- // 当插件被取消选中时执行
- }
-
- protected override async Task UnloadAsync()
- {
- // 当插件被卸载插件发生
- }
-
- }
复制代码 工具服务会进行自动发现,无需手动注册。
只需要将程序集放置在./plug-in目录下即可。
服务会在一个程序集中发现所有的插件类,并且进行注册。
按照上面的方式非常的简单就集成了插件。
开源地址
Gitee:https://gitee.com/gotrays/gotrays-suspension
Github:https://github.com/239573049/Suspension
技术交流群:737776595
来源:https://www.cnblogs.com/hejiale010426/archive/2023/07/02/17520719.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |