modified each other mode has own side and offset
This commit is contained in:
@@ -17,13 +17,23 @@ public sealed partial class AppSettings : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private PopupPlacementMode _placementMode = PopupPlacementMode.AtCursor;
|
private PopupPlacementMode _placementMode = PopupPlacementMode.AtCursor;
|
||||||
|
|
||||||
|
// Сторона и отступ хранятся отдельно для каждого режима: у курсора и у
|
||||||
|
// каретки удобны разные настройки, и переключение режима их не сбрасывает
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private CursorCorner _cursorCorner = CursorCorner.BottomRight;
|
private AnchorSide _cursorSide = AnchorSide.BottomRight;
|
||||||
|
|
||||||
/// <summary>Отступ от курсора в единицах WPF.</summary>
|
/// <summary>Отступ от курсора в единицах WPF.</summary>
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private double _cursorOffset = 16;
|
private double _cursorOffset = 16;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private AnchorSide _caretSide = AnchorSide.BottomRight;
|
||||||
|
|
||||||
|
/// <summary>Отступ от каретки в единицах WPF.</summary>
|
||||||
|
[ObservableProperty]
|
||||||
|
private double _caretOffset = 16;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ScreenPosition _screenPosition = ScreenPosition.BottomRight;
|
private ScreenPosition _screenPosition = ScreenPosition.BottomRight;
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ public enum PopupPlacementMode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// С какой стороны от курсора или каретки показывать подсказку.
|
/// С какой стороны от курсора или каретки показывать подсказку.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
public enum AnchorSide
|
||||||
public enum CursorCorner
|
|
||||||
{
|
{
|
||||||
BottomRight,
|
|
||||||
BottomLeft,
|
|
||||||
TopRight,
|
|
||||||
TopLeft,
|
TopLeft,
|
||||||
|
TopRight,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
BottomLeft,
|
||||||
|
BottomRight,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -85,18 +85,24 @@
|
|||||||
<data name="CursorCornerLabel" xml:space="preserve">
|
<data name="CursorCornerLabel" xml:space="preserve">
|
||||||
<value>Side</value>
|
<value>Side</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_BottomRight" xml:space="preserve">
|
<data name="AnchorSide_BottomRight" xml:space="preserve">
|
||||||
<value>Bottom right</value>
|
<value>Bottom right</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_BottomLeft" xml:space="preserve">
|
<data name="AnchorSide_BottomLeft" xml:space="preserve">
|
||||||
<value>Bottom left</value>
|
<value>Bottom left</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_TopRight" xml:space="preserve">
|
<data name="AnchorSide_TopRight" xml:space="preserve">
|
||||||
<value>Top right</value>
|
<value>Top right</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_TopLeft" xml:space="preserve">
|
<data name="AnchorSide_TopLeft" xml:space="preserve">
|
||||||
<value>Top left</value>
|
<value>Top left</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AnchorSide_Left" xml:space="preserve">
|
||||||
|
<value>Left</value>
|
||||||
|
</data>
|
||||||
|
<data name="AnchorSide_Right" xml:space="preserve">
|
||||||
|
<value>Right</value>
|
||||||
|
</data>
|
||||||
<data name="CursorOffsetLabel" xml:space="preserve">
|
<data name="CursorOffsetLabel" xml:space="preserve">
|
||||||
<value>Offset</value>
|
<value>Offset</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -85,18 +85,24 @@
|
|||||||
<data name="CursorCornerLabel" xml:space="preserve">
|
<data name="CursorCornerLabel" xml:space="preserve">
|
||||||
<value>Сторона</value>
|
<value>Сторона</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_BottomRight" xml:space="preserve">
|
<data name="AnchorSide_BottomRight" xml:space="preserve">
|
||||||
<value>Справа снизу</value>
|
<value>Справа снизу</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_BottomLeft" xml:space="preserve">
|
<data name="AnchorSide_BottomLeft" xml:space="preserve">
|
||||||
<value>Слева снизу</value>
|
<value>Слева снизу</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_TopRight" xml:space="preserve">
|
<data name="AnchorSide_TopRight" xml:space="preserve">
|
||||||
<value>Справа сверху</value>
|
<value>Справа сверху</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CursorCorner_TopLeft" xml:space="preserve">
|
<data name="AnchorSide_TopLeft" xml:space="preserve">
|
||||||
<value>Слева сверху</value>
|
<value>Слева сверху</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AnchorSide_Left" xml:space="preserve">
|
||||||
|
<value>Слева</value>
|
||||||
|
</data>
|
||||||
|
<data name="AnchorSide_Right" xml:space="preserve">
|
||||||
|
<value>Справа</value>
|
||||||
|
</data>
|
||||||
<data name="CursorOffsetLabel" xml:space="preserve">
|
<data name="CursorOffsetLabel" xml:space="preserve">
|
||||||
<value>Отступ</value>
|
<value>Отступ</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
|
|||||||
Localization.PropertyChanged += OnLocalizationChanged;
|
Localization.PropertyChanged += OnLocalizationChanged;
|
||||||
|
|
||||||
PlacementModes = CreateOptions<PopupPlacementMode>();
|
PlacementModes = CreateOptions<PopupPlacementMode>();
|
||||||
CursorCorners = CreateOptions<CursorCorner>();
|
AnchorSides = CreateOptions<AnchorSide>();
|
||||||
ScreenPositions = CreateOptions<ScreenPosition>();
|
ScreenPositions = CreateOptions<ScreenPosition>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
public IReadOnlyList<EnumOption<PopupPlacementMode>> PlacementModes { get; }
|
public IReadOnlyList<EnumOption<PopupPlacementMode>> PlacementModes { get; }
|
||||||
|
|
||||||
public IReadOnlyList<EnumOption<CursorCorner>> CursorCorners { get; }
|
public IReadOnlyList<EnumOption<AnchorSide>> AnchorSides { get; }
|
||||||
|
|
||||||
public IReadOnlyList<EnumOption<ScreenPosition>> ScreenPositions { get; }
|
public IReadOnlyList<EnumOption<ScreenPosition>> ScreenPositions { get; }
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
Translate(PlacementModes);
|
Translate(PlacementModes);
|
||||||
Translate(CursorCorners);
|
Translate(AnchorSides);
|
||||||
Translate(ScreenPositions);
|
Translate(ScreenPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,15 +109,30 @@ public partial class LayoutPopupWindow : Window
|
|||||||
var anchorPoint = new PopupWindowNative.Point { X = anchor.Left, Y = anchor.Top };
|
var anchorPoint = new PopupWindowNative.Point { X = anchor.Left, Y = anchor.Top };
|
||||||
double scale = PopupWindowNative.GetScaleAt(anchorPoint);
|
double scale = PopupWindowNative.GetScaleAt(anchorPoint);
|
||||||
|
|
||||||
|
// У каждого режима привязки своя сторона и свой отступ
|
||||||
|
bool atCaret = _settings.PlacementMode == PopupPlacementMode.AtCaret;
|
||||||
|
AnchorSide side = atCaret ? _settings.CaretSide : _settings.CursorSide;
|
||||||
|
int offset = ToPixels(atCaret ? _settings.CaretOffset : _settings.CursorOffset, scale);
|
||||||
|
|
||||||
int width = ToPixels(_contentSize.Width, scale);
|
int width = ToPixels(_contentSize.Width, scale);
|
||||||
int height = ToPixels(_contentSize.Height, scale);
|
int height = ToPixels(_contentSize.Height, scale);
|
||||||
int offset = ToPixels(_settings.CursorOffset, scale);
|
|
||||||
|
|
||||||
bool toRight = _settings.CursorCorner is CursorCorner.BottomRight or CursorCorner.TopRight;
|
int toLeftOf = anchor.Left - offset - width;
|
||||||
bool toBottom = _settings.CursorCorner is CursorCorner.BottomRight or CursorCorner.BottomLeft;
|
int toRightOf = anchor.Right + offset;
|
||||||
|
int above = anchor.Top - offset - height;
|
||||||
|
int below = anchor.Bottom + offset;
|
||||||
|
// Для сторон «слева» и «справа» подсказка встаёт вровень с точкой привязки
|
||||||
|
int middle = anchor.Top + (((anchor.Bottom - anchor.Top) - height) / 2);
|
||||||
|
|
||||||
int x = toRight ? anchor.Right + offset : anchor.Left - offset - width;
|
(int x, int y) = side switch
|
||||||
int y = toBottom ? anchor.Bottom + offset : anchor.Top - offset - height;
|
{
|
||||||
|
AnchorSide.TopLeft => (toLeftOf, above),
|
||||||
|
AnchorSide.TopRight => (toRightOf, above),
|
||||||
|
AnchorSide.Left => (toLeftOf, middle),
|
||||||
|
AnchorSide.Right => (toRightOf, middle),
|
||||||
|
AnchorSide.BottomLeft => (toLeftOf, below),
|
||||||
|
_ => (toRightOf, below),
|
||||||
|
};
|
||||||
|
|
||||||
PopupWindowNative.SetBounds(handle, x, y, width, height);
|
PopupWindowNative.SetBounds(handle, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,8 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<TextBlock Style="{StaticResource FieldLabel}"
|
<TextBlock Style="{StaticResource FieldLabel}"
|
||||||
@@ -81,49 +83,84 @@
|
|||||||
SelectedValuePath="Value"
|
SelectedValuePath="Value"
|
||||||
SelectedValue="{Binding Settings.PlacementMode}" />
|
SelectedValue="{Binding Settings.PlacementMode}" />
|
||||||
|
|
||||||
<!-- Привязка к точке: курсор или каретка -->
|
<!-- Режим «у курсора»: своя сторона и свой отступ -->
|
||||||
<TextBlock Grid.Row="1" Margin="0,12,12,0"
|
<TextBlock Grid.Row="1" Margin="0,12,12,0"
|
||||||
Style="{StaticResource FieldLabel}"
|
Style="{StaticResource FieldLabel}"
|
||||||
Text="{Binding Localization[CursorCornerLabel]}"
|
Text="{Binding Localization[CursorCornerLabel]}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter='AtCursor,AtCaret'}" />
|
ConverterParameter=AtCursor}" />
|
||||||
<ComboBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
|
<ComboBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
|
||||||
ItemsSource="{Binding CursorCorners}"
|
ItemsSource="{Binding AnchorSides}"
|
||||||
DisplayMemberPath="Display"
|
DisplayMemberPath="Display"
|
||||||
SelectedValuePath="Value"
|
SelectedValuePath="Value"
|
||||||
SelectedValue="{Binding Settings.CursorCorner}"
|
SelectedValue="{Binding Settings.CursorSide}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter='AtCursor,AtCaret'}" />
|
ConverterParameter=AtCursor}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Margin="0,12,12,0"
|
<TextBlock Grid.Row="2" Margin="0,12,12,0"
|
||||||
Style="{StaticResource FieldLabel}"
|
Style="{StaticResource FieldLabel}"
|
||||||
Text="{Binding Localization[CursorOffsetLabel]}"
|
Text="{Binding Localization[CursorOffsetLabel]}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter='AtCursor,AtCaret'}" />
|
ConverterParameter=AtCursor}" />
|
||||||
<Slider Grid.Row="2" Grid.Column="1" Margin="0,12,0,0"
|
<Slider Grid.Row="2" Grid.Column="1" Margin="0,12,0,0"
|
||||||
Minimum="0" Maximum="80" TickFrequency="1"
|
Minimum="0" Maximum="80" TickFrequency="1"
|
||||||
Value="{Binding Settings.CursorOffset}"
|
Value="{Binding Settings.CursorOffset}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter='AtCursor,AtCaret'}" />
|
ConverterParameter=AtCursor}" />
|
||||||
<TextBlock Grid.Row="2" Grid.Column="2" Margin="12,12,0,0"
|
<TextBlock Grid.Row="2" Grid.Column="2" Margin="12,12,0,0"
|
||||||
Style="{StaticResource FieldValue}"
|
Style="{StaticResource FieldValue}"
|
||||||
Text="{Binding Settings.CursorOffset, StringFormat={}{0:F0}}"
|
Text="{Binding Settings.CursorOffset, StringFormat={}{0:F0}}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter='AtCursor,AtCaret'}" />
|
ConverterParameter=AtCursor}" />
|
||||||
|
|
||||||
|
<!-- Режим «у каретки»: свои сторона и отступ -->
|
||||||
|
<TextBlock Grid.Row="3" Margin="0,12,12,0"
|
||||||
|
Style="{StaticResource FieldLabel}"
|
||||||
|
Text="{Binding Localization[CursorCornerLabel]}"
|
||||||
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
|
Converter={StaticResource EnumToVisibility},
|
||||||
|
ConverterParameter=AtCaret}" />
|
||||||
|
<ComboBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
|
||||||
|
ItemsSource="{Binding AnchorSides}"
|
||||||
|
DisplayMemberPath="Display"
|
||||||
|
SelectedValuePath="Value"
|
||||||
|
SelectedValue="{Binding Settings.CaretSide}"
|
||||||
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
|
Converter={StaticResource EnumToVisibility},
|
||||||
|
ConverterParameter=AtCaret}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Margin="0,12,12,0"
|
||||||
|
Style="{StaticResource FieldLabel}"
|
||||||
|
Text="{Binding Localization[CursorOffsetLabel]}"
|
||||||
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
|
Converter={StaticResource EnumToVisibility},
|
||||||
|
ConverterParameter=AtCaret}" />
|
||||||
|
<Slider Grid.Row="4" Grid.Column="1" Margin="0,12,0,0"
|
||||||
|
Minimum="0" Maximum="80" TickFrequency="1"
|
||||||
|
Value="{Binding Settings.CaretOffset}"
|
||||||
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
|
Converter={StaticResource EnumToVisibility},
|
||||||
|
ConverterParameter=AtCaret}" />
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="2" Margin="12,12,0,0"
|
||||||
|
Style="{StaticResource FieldValue}"
|
||||||
|
Text="{Binding Settings.CaretOffset, StringFormat={}{0:F0}}"
|
||||||
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
|
Converter={StaticResource EnumToVisibility},
|
||||||
|
ConverterParameter=AtCaret}" />
|
||||||
|
|
||||||
<!-- Режим «фиксированная точка» -->
|
<!-- Режим «фиксированная точка» -->
|
||||||
<TextBlock Grid.Row="3" Margin="0,12,12,0"
|
<TextBlock Grid.Row="5" Margin="0,12,12,0"
|
||||||
Style="{StaticResource FieldLabel}"
|
Style="{StaticResource FieldLabel}"
|
||||||
Text="{Binding Localization[ScreenPositionLabel]}"
|
Text="{Binding Localization[ScreenPositionLabel]}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter=FixedPoint}" />
|
ConverterParameter=FixedPoint}" />
|
||||||
<ComboBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
|
<ComboBox Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,12,0,0"
|
||||||
ItemsSource="{Binding ScreenPositions}"
|
ItemsSource="{Binding ScreenPositions}"
|
||||||
DisplayMemberPath="Display"
|
DisplayMemberPath="Display"
|
||||||
SelectedValuePath="Value"
|
SelectedValuePath="Value"
|
||||||
@@ -132,19 +169,19 @@
|
|||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter=FixedPoint}" />
|
ConverterParameter=FixedPoint}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="4" Margin="0,12,12,0"
|
<TextBlock Grid.Row="6" Margin="0,12,12,0"
|
||||||
Style="{StaticResource FieldLabel}"
|
Style="{StaticResource FieldLabel}"
|
||||||
Text="{Binding Localization[ScreenMarginLabel]}"
|
Text="{Binding Localization[ScreenMarginLabel]}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter=FixedPoint}" />
|
ConverterParameter=FixedPoint}" />
|
||||||
<Slider Grid.Row="4" Grid.Column="1" Margin="0,12,0,0"
|
<Slider Grid.Row="6" Grid.Column="1" Margin="0,12,0,0"
|
||||||
Minimum="0" Maximum="200" TickFrequency="1"
|
Minimum="0" Maximum="200" TickFrequency="1"
|
||||||
Value="{Binding Settings.ScreenMargin}"
|
Value="{Binding Settings.ScreenMargin}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
Converter={StaticResource EnumToVisibility},
|
Converter={StaticResource EnumToVisibility},
|
||||||
ConverterParameter=FixedPoint}" />
|
ConverterParameter=FixedPoint}" />
|
||||||
<TextBlock Grid.Row="4" Grid.Column="2" Margin="12,12,0,0"
|
<TextBlock Grid.Row="6" Grid.Column="2" Margin="12,12,0,0"
|
||||||
Style="{StaticResource FieldValue}"
|
Style="{StaticResource FieldValue}"
|
||||||
Text="{Binding Settings.ScreenMargin, StringFormat={}{0:F0}}"
|
Text="{Binding Settings.ScreenMargin, StringFormat={}{0:F0}}"
|
||||||
Visibility="{Binding Settings.PlacementMode,
|
Visibility="{Binding Settings.PlacementMode,
|
||||||
|
|||||||
Reference in New Issue
Block a user