feat: 新增 Android 移动端 head 与共享移动壳(组件面板)

This commit is contained in:
lincube
2026-07-26 22:28:25 +09:00
parent 9b7a35ddd3
commit 41c484eaf8
12 changed files with 253 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Settings.Core\LanMountainDesktop.Settings.Core.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Appearance\LanMountainDesktop.Appearance.csproj" />
<ProjectReference Include="..\LanMountainDesktop.DesktopComponents.Runtime\LanMountainDesktop.DesktopComponents.Runtime.csproj" />
<ProjectReference Include="..\LanMountainDesktop.PluginSdk\LanMountainDesktop.PluginSdk.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" />
<PackageReference Include="Avalonia.Themes.Fluent" />
<PackageReference Include="Avalonia.Fonts.Inter" />
<PackageReference Include="CommunityToolkit.Mvvm" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,8 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="LanMountainDesktop.Mobile.MobileApp"
RequestedThemeVariant="Default">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@@ -0,0 +1,33 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using LanMountainDesktop.Mobile.ViewModels;
using LanMountainDesktop.Mobile.Views;
namespace LanMountainDesktop.Mobile;
/// <summary>
/// 移动端应用入口。以单视图生命周期承载组件面板。
/// 与桌面宿主 (LanMountainDesktop.App) 共享契约、外观与组件运行时,
/// 但不包含桌面层、托盘、进程外插件等桌面专属能力。
/// </summary>
public partial class MobileApp : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is ISingleViewApplicationLifetime singleView)
{
singleView.MainView = new WidgetPanelView
{
DataContext = new WidgetPanelViewModel()
};
}
base.OnFrameworkInitializationCompleted();
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace LanMountainDesktop.Mobile.ViewModels;
/// <summary>
/// 组件面板视图模型。
/// 移动端不提供桌面自由摆放,组件以卡片流形式呈现。
/// 后续由组件运行时DesktopComponents.Runtime通过
/// <see cref="RegisterWidget"/> 注入真实组件视图。
/// </summary>
public sealed partial class WidgetPanelViewModel : ObservableObject
{
public ObservableCollection<WidgetCardViewModel> Widgets { get; } = [];
[ObservableProperty]
private string _title = "组件面板";
public WidgetPanelViewModel()
{
// 首版占位卡片:标记组件运行时的接入点。
Widgets.Add(new WidgetCardViewModel("时间表", "课程表 / 日程组件将在此呈现"));
Widgets.Add(new WidgetCardViewModel("天气", "天气组件将在此呈现"));
Widgets.Add(new WidgetCardViewModel("插件组件", "已安装插件提供的组件将在此呈现"));
}
/// <summary>
/// 组件运行时接入点:注册一个真实组件卡片。
/// </summary>
public void RegisterWidget(WidgetCardViewModel widget)
{
Widgets.Add(widget);
}
}
/// <summary>
/// 单个组件卡片。<see cref="Content"/> 为空时显示占位说明文本,
/// 非空时承载组件运行时提供的真实控件。
/// </summary>
public sealed partial class WidgetCardViewModel : ObservableObject
{
[ObservableProperty]
private string _name;
[ObservableProperty]
private string _placeholder;
[ObservableProperty]
private object? _content;
public WidgetCardViewModel(string name, string placeholder, object? content = null)
{
_name = name;
_placeholder = placeholder;
_content = content;
}
}

View File

@@ -0,0 +1,44 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:LanMountainDesktop.Mobile.ViewModels"
x:Class="LanMountainDesktop.Mobile.Views.WidgetPanelView"
x:DataType="vm:WidgetPanelViewModel">
<Grid RowDefinitions="Auto,*">
<Border Grid.Row="0" Padding="20,16">
<TextBlock Text="{Binding Title}"
FontSize="22"
FontWeight="SemiBold" />
</Border>
<ScrollViewer Grid.Row="1" Padding="12,0,12,12">
<ItemsControl ItemsSource="{Binding Widgets}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="12" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:WidgetCardViewModel">
<Border Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
CornerRadius="16"
Padding="16"
MinHeight="96">
<StackPanel Spacing="8">
<TextBlock Text="{Binding Name}"
FontSize="15"
FontWeight="SemiBold" />
<ContentControl Content="{Binding Content}"
IsVisible="{Binding Content, Converter={x:Static ObjectConverters.IsNotNull}}" />
<TextBlock Text="{Binding Placeholder}"
FontSize="13"
Opacity="0.6"
TextWrapping="Wrap"
IsVisible="{Binding Content, Converter={x:Static ObjectConverters.IsNull}}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace LanMountainDesktop.Mobile.Views;
public partial class WidgetPanelView : UserControl
{
public WidgetPanelView()
{
InitializeComponent();
}
}