Skip to content

Commit d4ba3bd

Browse files
committed
add: 初步实现终端的显示逻辑
1 parent 565f86b commit d4ba3bd

File tree

9 files changed

+430
-74
lines changed

9 files changed

+430
-74
lines changed

llcomNext/LLCOM/LLCOM.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
3838
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.3" />
3939
<PackageReference Include="System.Text.Json" Version="9.0.3" />
40+
<PackageReference Include="Wcwidth" Version="2.0.0" />
4041
</ItemGroup>
4142

4243

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using Avalonia.Media;
3+
using Wcwidth;
4+
5+
namespace LLCOM.Models;
6+
7+
/// <summary>
8+
/// 一个终端块,包含各项信息
9+
/// </summary>
10+
public class TerminalBlock
11+
{
12+
//颜色均只存储颜色值,30~37、90~97为颜色值,0为默认颜色
13+
14+
public int Background { get; set; }
15+
public int Foreground { get; set; }
16+
17+
public static string Color2BindingName(int color, bool isForeground)
18+
{
19+
if(color == 0)
20+
return isForeground ? "TerminalTheme.Foreground" : "TerminalTheme.Background";
21+
return "TerminalTheme.Code" + color.ToString();
22+
}
23+
public string ForegroundBindingName => Color2BindingName(Foreground, true);
24+
public string BackgroundBindingName => Color2BindingName(Background, false);
25+
26+
27+
public bool IsBold { get; set; }
28+
public bool IsUnderLine { get; set; }
29+
public bool IsItalic { get; set; }
30+
31+
//文字数据里不应包含不可见字符,比如换行符、回车符等
32+
private string _text = String.Empty;
33+
public string Text
34+
{
35+
get => _text;
36+
set
37+
{
38+
_text = value;
39+
//计算长度
40+
_length = 0;
41+
foreach (var c in _text)
42+
{
43+
_length += UnicodeCalculator.GetWidth(c);
44+
}
45+
}
46+
}
47+
48+
private int _length = 0;
49+
50+
public int Length => _length;
51+
52+
53+
/// <summary>
54+
/// 一个终端块,包含各项信息
55+
/// </summary>
56+
public TerminalBlock(string text,
57+
int background = 0,
58+
int foreground = 0,
59+
bool isBold = false,
60+
bool isUnderLine = false,
61+
bool isItalic = false)
62+
{
63+
Text = text;
64+
Background = background;
65+
Foreground = foreground;
66+
IsBold = isBold;
67+
IsUnderLine = isUnderLine;
68+
IsItalic = isItalic;
69+
}
70+
}

llcomNext/LLCOM/Services/Setting.cs

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations;
34
using System.Diagnostics;
45
using System.Linq;
56
using System.Threading.Tasks;
@@ -125,7 +126,7 @@ public FontFamily? TerminalFontFamily
125126
get
126127
{
127128
if(string.IsNullOrEmpty(TerminalFont))
128-
TerminalFont = Utils.CheckFontName(TerminalFont);
129+
TerminalFont = Utils.CheckMonoFontName(TerminalFont);
129130
return TerminalFont;
130131
}
131132
set
@@ -136,10 +137,29 @@ public FontFamily? TerminalFontFamily
136137
OnPropertyChanged();
137138
}
138139
}
139-
//字体名称
140+
//终端模式字体名称
140141
[ObservableProperty]
141142
private string _terminalFont = Database.Get(nameof(TerminalFont), "").Result;
142-
143+
//终端模式的缓冲区行数
144+
[ObservableProperty]
145+
private int _terminalBufferLines = Database.Get(nameof(TerminalBufferLines), 5000).Result;
146+
//终端模式的字体大小
147+
private int _terminalFontSize = Database.Get(nameof(TerminalFontSize), 16).Result;
148+
[Range(14,50)]
149+
public int TerminalFontSize
150+
{
151+
get => _terminalFontSize;
152+
set
153+
{
154+
_terminalFontSize = value;
155+
OnPropertyChanged();
156+
TerminalChangedEvent?.Invoke(this, EventArgs.Empty);
157+
}
158+
}
159+
//用于通知字体大小改变的事件
160+
public EventHandler? TerminalChangedEvent = null;
161+
partial void OnTerminalFontChanged(string _) => TerminalChangedEvent?.Invoke(this, EventArgs.Empty);
162+
143163

144164
//终端模式的主题
145165
private TerminalColorScheme? _terminalTheme = null;
@@ -182,6 +202,7 @@ public Setting()
182202
nameof(PacketHeaderFontFamily),
183203
nameof(PacketExtraFontFamily),
184204
nameof(TerminalFontFamily),
205+
nameof(TerminalChangedEvent),
185206
];
186207
if (name == null || dismissList.Contains(name))
187208
return;

llcomNext/LLCOM/Services/Utils.cs

+85
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,89 @@ public static void OpenWebLink(string url)
160160
/// <returns>可用的字体名</returns>
161161
public static string CheckFontName(string name) =>
162162
(SkiaSharp.SKFontManager.Default.MatchFamily(name) ?? SkiaSharp.SKTypeface.Default).FamilyName;
163+
164+
/// <summary>
165+
/// 获取字体名称,会根据字体名称返回一个可用的字体名称
166+
/// 如果字体名称不存在,则返回默认字体名称
167+
/// </summary>
168+
/// <param name="name">字体名</param>
169+
/// <returns>可用的字体名</returns>
170+
public static string CheckMonoFontName(string name)
171+
{
172+
var fontMgr = SkiaSharp.SKFontManager.Default;
173+
var font = fontMgr.MatchFamily(name) ?? SkiaSharp.SKTypeface.Default;
174+
if (font.IsFixedPitch)//是等宽字体
175+
return font.FamilyName;
176+
177+
//不同的操作系统下的等宽字体都试试
178+
string[] defaultFontList =
179+
[
180+
"Consolas",
181+
"Menlo",
182+
"Monaco",
183+
"DejaVu Sans Mono",
184+
"Liberation Mono",
185+
"Ubuntu Mono",
186+
"Source Code Pro",
187+
"Fira Code",
188+
"Courier New",
189+
"Courier",
190+
];
191+
foreach (var defaultFont in defaultFontList)
192+
{
193+
font = fontMgr.MatchFamily(defaultFont);
194+
if (font == null)
195+
continue;
196+
if (font.IsFixedPitch)
197+
return font.FamilyName;
198+
}
199+
//如果没有找到等宽字体,返回第一个等宽字体
200+
foreach (var f in fontMgr.FontFamilies)
201+
{
202+
font = fontMgr.MatchFamily(f);
203+
if (font.IsFixedPitch)
204+
return font.FamilyName;
205+
}
206+
//如果还是没有找到,返回默认字体
207+
return SkiaSharp.SKTypeface.Default.FamilyName;
208+
}
209+
210+
/// <summary>
211+
/// 计算终端窗口可以显示的行数和列数
212+
/// </summary>
213+
/// <param name="width">窗口宽度</param>
214+
/// <param name="height">窗口高度</param>
215+
/// <param name="font">字体名称</param>
216+
/// <param name="fontSize">字体大小</param>
217+
/// <returns>(列数宽度,行数高度)</returns>
218+
public static (int, int) CalculateSize(double width, double height, string font,double fontSize)
219+
{
220+
//根据当前字体大小和窗口大小,计算出可以显示的行数和列数
221+
//按实际的字符宽度和高度来计算
222+
var fontFamily = SkiaSharp.SKFontManager.Default.MatchFamily(font) ?? SkiaSharp.SKTypeface.Default;
223+
224+
//计算出每个字符的宽度和高度,用skiasharp的接口来计算
225+
//主要字符之间会有间距,所以需要用实际的字符来计算
226+
var paint = new SkiaSharp.SKPaint
227+
{
228+
Typeface = fontFamily,
229+
TextSize = (float)fontSize
230+
};
231+
232+
//计算出一行能放下多少个字符,暂时用最宽的字符来计算
233+
var charWidth = paint.MeasureText("W");
234+
var charSpacing = paint.FontMetrics.Leading;
235+
int columns = (int)(width / (charWidth + charSpacing));
236+
237+
//计算出能放下多少行
238+
var charHeight = Math.Abs(paint.FontMetrics.Ascent - paint.FontMetrics.Descent + paint.FontMetrics.Leading);
239+
int rows = (int)(height / charHeight);
240+
241+
//注意不要让行数和列数为0
242+
if (rows <= 0) rows = 1;
243+
if (columns <= 0) columns = 1;
244+
245+
//返回
246+
return (columns, rows);
247+
}
163248
}

llcomNext/LLCOM/ViewModels/DataViews/TerminalViewModel.cs

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
4+
using System.Text;
5+
using CommunityToolkit.Mvvm.ComponentModel;
6+
using CommunityToolkit.Mvvm.Input;
7+
using LLCOM.Models;
8+
using LLCOM.Services;
29

310
namespace LLCOM.ViewModels;
411

llcomNext/LLCOM/ViewModels/Pages/SettingPageViewModel.cs

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Collections.ObjectModel;
4+
using System.Threading;
45
using System.Threading.Tasks;
56
using Avalonia.Controls;
67
using Avalonia.Media;
@@ -69,6 +70,8 @@ public SettingPageViewModel(Func<Type, ViewModelBase> getService)
6970
[ObservableProperty]
7071
private ObservableCollection<FontFamily> _fontList = new();
7172
[ObservableProperty]
73+
private ObservableCollection<FontFamily> _monoFontList = new();
74+
[ObservableProperty]
7275
private int _packetFontIndex;
7376
[ObservableProperty]
7477
private int _packetHexFontIndex;
@@ -85,32 +88,42 @@ private void RefreshFontIndex()
8588
//用skia接口获取系统字体列表
8689
var fontMgr = SkiaSharp.SKFontManager.Default;
8790
var fonts = new List<string>();
91+
var monoFonts = new List<string>();
8892
foreach (var f in fontMgr.FontFamilies)
8993
{
90-
if (IsMonoFont)
91-
{
92-
using var typeface = fontMgr.MatchFamily(f) ;
93-
if (typeface.IsFixedPitch)
94-
fonts.Add(f);
95-
}
96-
else
97-
{
98-
fonts.Add(f);
99-
}
94+
using var typeface = fontMgr.MatchFamily(f) ;
95+
if (typeface.IsFixedPitch)
96+
monoFonts.Add(f);
97+
fonts.Add(f);
10098
}
10199
//把列表内容按字母排序
102100
fonts.Sort();
101+
monoFonts.Sort();
103102
//添加到系统字体列表
104103
FontList.Clear();
105-
foreach (var f in fonts)
106-
FontList.Add(f);
104+
if (IsMonoFont)
105+
{
106+
foreach (var f in monoFonts)
107+
FontList.Add(f);
108+
}
109+
else
110+
{
111+
foreach (var f in fonts)
112+
FontList.Add(f);
113+
}
114+
115+
if (MonoFontList.Count == 0)
116+
{
117+
foreach (var f in monoFonts)
118+
MonoFontList.Add(f);
119+
}
107120

108121
//刷新字体索引
109122
PacketFontIndex = FontList.IndexOf(Services.Utils.Setting.PacketFontFamily ?? "");
110123
PacketHexFontIndex = FontList.IndexOf(Services.Utils.Setting.PacketHexFontFamily?? "");
111124
PacketHeaderFontIndex = FontList.IndexOf(Services.Utils.Setting.PacketHeaderFontFamily?? "");
112125
PacketExtraFontIndex = FontList.IndexOf(Services.Utils.Setting.PacketExtraFontFamily?? "");
113-
TerminalFontIndex = FontList.IndexOf(Services.Utils.Setting.TerminalFontFamily?? "");
126+
TerminalFontIndex = MonoFontList.IndexOf(Services.Utils.Setting.TerminalFontFamily?? "");
114127
}
115128

116129
#endregion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,41 @@
11
<UserControl
2-
d:DesignHeight="757"
3-
d:DesignWidth="762"
4-
mc:Ignorable="d"
52
x:Class="LLCOM.Views.TerminalView"
6-
x:DataType="vm:TerminalViewModel"
73
xmlns="https://github.com/avaloniaui"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
85
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
96
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
7+
xmlns:services="clr-namespace:LLCOM.Services"
108
xmlns:vm="using:LLCOM.ViewModels"
11-
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
9+
d:DesignHeight="757"
10+
d:DesignWidth="762"
11+
x:DataType="vm:TerminalViewModel"
12+
Background="{Binding TerminalTheme.Background, Source={x:Static services:Utils.Setting}}"
13+
Loaded="Control_OnLoaded"
14+
mc:Ignorable="d">
1215
<Design.DataContext>
1316
<vm:TerminalViewModel />
1417
</Design.DataContext>
15-
TerminalView
18+
<!--
19+
目前不打算支持操作光标位置、退格
20+
终端只负责不断打印新数据
21+
-->
22+
<Panel
23+
Name="MainArea"
24+
Margin="5"
25+
HorizontalAlignment="Stretch"
26+
VerticalAlignment="Stretch"
27+
PointerWheelChanged="MainArea_OnPointerWheelChanged">
28+
<SelectableTextBlock
29+
Name="MainTextBlock"
30+
Background="Transparent"
31+
FontFamily="{Binding TerminalFontFamily, Source={x:Static services:Utils.Setting}}"
32+
FontSize="{Binding TerminalFontSize, Source={x:Static services:Utils.Setting}}"
33+
SelectionBrush="{Binding TerminalTheme.SelectionBackground, Source={x:Static services:Utils.Setting}}"
34+
SelectionForegroundBrush="{Binding TerminalTheme.Background, Source={x:Static services:Utils.Setting}}" />
35+
<ScrollBar
36+
Name="MainScrollBar"
37+
HorizontalAlignment="Right"
38+
Scroll="MainScrollBar_OnScroll"
39+
Visibility="Visible" />
40+
</Panel>
1641
</UserControl>

0 commit comments

Comments
 (0)