moved to MVVM architecture

This commit is contained in:
2026-08-08 21:24:02 +05:00
parent a1ff8cc923
commit 3d614255d8
19 changed files with 565 additions and 346 deletions
+26
View File
@@ -0,0 +1,26 @@
<Window x:Class="CursorLang.Views.LayoutPopupWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:CursorLang.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:LayoutPopupViewModel}"
WindowStyle="None"
ResizeMode="NoResize"
AllowsTransparency="True"
Background="Transparent"
ShowInTaskbar="False"
ShowActivated="False"
Topmost="True"
Focusable="False"
IsHitTestVisible="False">
<Border Background="#E6202020"
CornerRadius="4"
Padding="10,4">
<TextBlock Foreground="White"
FontSize="20"
FontWeight="SemiBold"
Text="{Binding ShortName}" />
</Border>
</Window>
@@ -0,0 +1,75 @@
using System.Windows;
using System.Windows.Interop;
using CursorLang.Interop;
using CursorLang.Services;
using CursorLang.ViewModels;
namespace CursorLang.Views;
/// <summary>
/// Всплывающая подсказка у курсора с коротким именем раскладки.
/// Отвечает только за показ: когда её убрать, решает <see cref="LayoutPopupService"/>.
/// </summary>
public partial class LayoutPopupWindow : Window
{
private readonly double _cursorOffset;
private PopupWindowNative.Point _cursor;
public LayoutPopupWindow(LayoutPopupViewModel viewModel, PopupOptions options)
{
InitializeComponent();
DataContext = viewModel;
_cursorOffset = options.CursorOffset;
}
/// <summary>
/// Показывает подсказку у текущего положения курсора.
/// </summary>
public void ShowAtCursor()
{
ResizeToContent();
_cursor = PopupWindowNative.GetCursorPosition();
// Для уже созданного окна двигаем до показа; при самом первом вызове
// хэндла ещё нет, и позиционирование выполнит OnSourceInitialized
MoveToCursor();
Show();
}
// Хэндл создан, но окно ещё не отрисовано: здесь и настраиваем стили,
// и ставим окно на место, чтобы первый показ прошёл уже на нужном мониторе
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
PopupWindowNative.MakePassive(new WindowInteropHelper(this).Handle);
MoveToCursor();
}
// Размер считаем сами, а не через SizeToContent: тот вычисляет его при создании
// окна, когда содержимое ещё не измерено, и первый показ выходит шире текста.
// Рамок у окна нет, поэтому его размер равен размеру содержимого
private void ResizeToContent()
{
var content = (FrameworkElement)Content;
content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Width = content.DesiredSize.Width;
Height = content.DesiredSize.Height;
}
private void MoveToCursor()
{
IntPtr handle = new WindowInteropHelper(this).Handle;
if (handle == IntPtr.Zero)
{
return;
}
// Отступ задан в единицах WPF, а двигаем окно в пикселях: пересчитываем
// его по масштабу монитора, на котором сейчас курсор
int offset = (int)Math.Round(_cursorOffset * PopupWindowNative.GetScaleAt(_cursor));
PopupWindowNative.MoveTo(handle, _cursor.X + offset, _cursor.Y + offset);
}
}
+16
View File
@@ -0,0 +1,16 @@
<Window x:Class="CursorLang.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:CursorLang.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Label HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="48"
Content="{Binding CurrentLayout}" />
</Grid>
</Window>
+16
View File
@@ -0,0 +1,16 @@
using System.Windows;
using CursorLang.ViewModels;
namespace CursorLang.Views;
/// <summary>
/// Главное окно: показывает текущую раскладку.
/// </summary>
public partial class MainWindow : Window
{
public MainWindow(MainViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}