fixed bug - change tooltip size when change font size
This commit is contained in:
@@ -101,6 +101,20 @@ internal static class PopupWindowNative
|
|||||||
SetWindowPos(hWnd, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
SetWindowPos(hWnd, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Задаёт положение и размер окна в физических пикселях.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Размер выставляется именно так, а не через Width/Height: при первом показе
|
||||||
|
/// окно ещё подчиняется системному минимальному размеру окна (SM_CXMIN×SM_CYMIN)
|
||||||
|
/// и подсказка выходит заметно крупнее текста. К моменту вызова окно уже
|
||||||
|
/// показано и стало popup-окном, на которое это ограничение не действует.
|
||||||
|
/// </remarks>
|
||||||
|
internal static void SetBounds(IntPtr hWnd, int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
SetWindowPos(hWnd, IntPtr.Zero, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Масштаб монитора, на котором находится точка (1.0 при 96 DPI).</summary>
|
/// <summary>Масштаб монитора, на котором находится точка (1.0 при 96 DPI).</summary>
|
||||||
internal static double GetScaleAt(Point point) =>
|
internal static double GetScaleAt(Point point) =>
|
||||||
GetScaleOf(MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST));
|
GetScaleOf(MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST));
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ namespace CursorLang.Views;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Всплывающая подсказка с коротким именем раскладки.
|
/// Всплывающая подсказка с коротким именем раскладки.
|
||||||
/// Отвечает только за показ и место на экране: когда её убрать,
|
/// Отвечает только за показ, размер и место на экране: когда её убрать,
|
||||||
/// решает <see cref="Services.LayoutPopupService"/>.
|
/// решает <see cref="Services.LayoutPopupService"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class LayoutPopupWindow : Window
|
public partial class LayoutPopupWindow : Window
|
||||||
{
|
{
|
||||||
private readonly AppSettings _settings;
|
private readonly AppSettings _settings;
|
||||||
|
private Size _contentSize;
|
||||||
|
|
||||||
public LayoutPopupWindow(LayoutPopupViewModel viewModel, AppSettings settings)
|
public LayoutPopupWindow(LayoutPopupViewModel viewModel, AppSettings settings)
|
||||||
{
|
{
|
||||||
@@ -28,37 +29,40 @@ public partial class LayoutPopupWindow : Window
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ShowPopup()
|
public void ShowPopup()
|
||||||
{
|
{
|
||||||
ResizeToContent();
|
MeasureContent();
|
||||||
|
|
||||||
// Для уже созданного окна двигаем до показа; при самом первом вызове
|
// Уже созданное окно ставим на место до показа, чтобы оно не мелькнуло
|
||||||
// хэндла ещё нет, и позиционирование выполнит OnSourceInitialized
|
// на старой позиции; при самом первом вызове хэндла ещё нет
|
||||||
MoveToTargetPosition();
|
ApplyBounds();
|
||||||
Show();
|
Show();
|
||||||
|
|
||||||
|
// Повторяем после показа: до него окно подчиняется системному минимальному
|
||||||
|
// размеру окна и получается заметно крупнее текста
|
||||||
|
ApplyBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Хэндл создан, но окно ещё не отрисовано: здесь и настраиваем стили,
|
|
||||||
// и ставим окно на место, чтобы первый показ прошёл уже на нужном мониторе
|
|
||||||
protected override void OnSourceInitialized(EventArgs e)
|
protected override void OnSourceInitialized(EventArgs e)
|
||||||
{
|
{
|
||||||
base.OnSourceInitialized(e);
|
base.OnSourceInitialized(e);
|
||||||
|
|
||||||
PopupWindowNative.MakePassive(new WindowInteropHelper(this).Handle);
|
PopupWindowNative.MakePassive(new WindowInteropHelper(this).Handle);
|
||||||
MoveToTargetPosition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Размер считаем сами, а не через SizeToContent: тот вычисляет его при создании
|
// Размер содержимого нужен до показа: от него зависит и размер окна,
|
||||||
// окна, когда содержимое ещё не измерено, и первый показ выходит шире текста.
|
// и позиция для углов. Рамок у окна нет, поэтому размер окна равен размеру
|
||||||
// Рамок у окна нет, поэтому его размер равен размеру содержимого
|
// содержимого
|
||||||
private void ResizeToContent()
|
private void MeasureContent()
|
||||||
{
|
{
|
||||||
var content = (FrameworkElement)Content;
|
var content = (FrameworkElement)Content;
|
||||||
content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||||
|
|
||||||
Width = content.DesiredSize.Width;
|
_contentSize = content.DesiredSize;
|
||||||
Height = content.DesiredSize.Height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoveToTargetPosition()
|
// Всё считается в физических пикселях: у мониторов разный масштаб, а
|
||||||
|
// Window.Left/Top/Width/Height пересчитываются по DPI того монитора,
|
||||||
|
// где окно сейчас, и на соседнем мониторе дают промах
|
||||||
|
private void ApplyBounds()
|
||||||
{
|
{
|
||||||
IntPtr handle = new WindowInteropHelper(this).Handle;
|
IntPtr handle = new WindowInteropHelper(this).Handle;
|
||||||
if (handle == IntPtr.Zero)
|
if (handle == IntPtr.Zero)
|
||||||
@@ -66,37 +70,43 @@ public partial class LayoutPopupWindow : Window
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(int x, int y) = _settings.PlacementMode == PopupPlacementMode.AtCursor
|
if (_settings.PlacementMode == PopupPlacementMode.AtCursor)
|
||||||
? GetCursorPosition()
|
{
|
||||||
: GetScreenPosition();
|
ApplyBoundsAtCursor(handle);
|
||||||
|
}
|
||||||
PopupWindowNative.MoveTo(handle, x, y);
|
else
|
||||||
|
{
|
||||||
|
ApplyBoundsOnScreen(handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отступы и размеры заданы в единицах WPF, а окно двигаем в пикселях,
|
private void ApplyBoundsAtCursor(IntPtr handle)
|
||||||
// поэтому всё пересчитывается по масштабу нужного монитора
|
|
||||||
private (int X, int Y) GetCursorPosition()
|
|
||||||
{
|
{
|
||||||
PopupWindowNative.Point cursor = PopupWindowNative.GetCursorPosition();
|
PopupWindowNative.Point cursor = PopupWindowNative.GetCursorPosition();
|
||||||
double scale = PopupWindowNative.GetScaleAt(cursor);
|
double scale = PopupWindowNative.GetScaleAt(cursor);
|
||||||
int offset = (int)Math.Round(_settings.CursorOffset * scale);
|
|
||||||
|
int width = ToPixels(_contentSize.Width, scale);
|
||||||
|
int height = ToPixels(_contentSize.Height, scale);
|
||||||
|
int offset = ToPixels(_settings.CursorOffset, scale);
|
||||||
|
|
||||||
bool toRight = _settings.CursorCorner is CursorCorner.BottomRight or CursorCorner.TopRight;
|
bool toRight = _settings.CursorCorner is CursorCorner.BottomRight or CursorCorner.TopRight;
|
||||||
bool toBottom = _settings.CursorCorner is CursorCorner.BottomRight or CursorCorner.BottomLeft;
|
bool toBottom = _settings.CursorCorner is CursorCorner.BottomRight or CursorCorner.BottomLeft;
|
||||||
|
|
||||||
int x = toRight ? cursor.X + offset : cursor.X - offset - ToPixels(Width, scale);
|
int x = toRight ? cursor.X + offset : cursor.X - offset - width;
|
||||||
int y = toBottom ? cursor.Y + offset : cursor.Y - offset - ToPixels(Height, scale);
|
int y = toBottom ? cursor.Y + offset : cursor.Y - offset - height;
|
||||||
return (x, y);
|
|
||||||
|
PopupWindowNative.SetBounds(handle, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private (int X, int Y) GetScreenPosition()
|
private void ApplyBoundsOnScreen(IntPtr handle)
|
||||||
{
|
{
|
||||||
(PopupWindowNative.Rect work, double scale) = PopupWindowNative.GetActiveMonitorWorkArea();
|
(PopupWindowNative.Rect work, double scale) = PopupWindowNative.GetActiveMonitorWorkArea();
|
||||||
int margin = (int)Math.Round(_settings.ScreenMargin * scale);
|
|
||||||
int width = ToPixels(Width, scale);
|
|
||||||
int height = ToPixels(Height, scale);
|
|
||||||
|
|
||||||
return _settings.ScreenPosition switch
|
int width = ToPixels(_contentSize.Width, scale);
|
||||||
|
int height = ToPixels(_contentSize.Height, scale);
|
||||||
|
int margin = ToPixels(_settings.ScreenMargin, scale);
|
||||||
|
|
||||||
|
(int x, int y) = _settings.ScreenPosition switch
|
||||||
{
|
{
|
||||||
ScreenPosition.TopLeft => (work.Left + margin, work.Top + margin),
|
ScreenPosition.TopLeft => (work.Left + margin, work.Top + margin),
|
||||||
ScreenPosition.TopRight => (work.Right - margin - width, work.Top + margin),
|
ScreenPosition.TopRight => (work.Right - margin - width, work.Top + margin),
|
||||||
@@ -105,6 +115,8 @@ public partial class LayoutPopupWindow : Window
|
|||||||
_ => (work.Left + ((work.Right - work.Left - width) / 2),
|
_ => (work.Left + ((work.Right - work.Left - width) / 2),
|
||||||
work.Top + ((work.Bottom - work.Top - height) / 2)),
|
work.Top + ((work.Bottom - work.Top - height) / 2)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PopupWindowNative.SetBounds(handle, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ToPixels(double wpfUnits, double scale) => (int)Math.Round(wpfUnits * scale);
|
private static int ToPixels(double wpfUnits, double scale) => (int)Math.Round(wpfUnits * scale);
|
||||||
|
|||||||
Reference in New Issue
Block a user