using System.Drawing; using System.Text.Json.Serialization; using CommunityToolkit.Mvvm.ComponentModel; namespace CursorLang.Core.Models; /// /// The settings of one placement mode: where the popup goes and how it looks there. /// /// /// Every mode keeps a set of its own. The popup next to the caret sits inside a text /// being read and is wanted small and quiet; the one in the corner of the monitor is /// looked for on purpose and is wanted large. A look shared by the modes meant setting /// it up again after every switch — and switching modes to see what they do undid what /// had just been set up. /// /// The colours are for the same reason as in /// : the agent reads them and must stay clear of WPF. /// public abstract partial class PopupModeSettings : ObservableObject { /// The distance from whatever the popup is placed by, in WPF units. [ObservableProperty] private double _offset = 16; /// The size of the layout name in the popup, in WPF units. [ObservableProperty] private double _fontSize = 20; /// The popup opacity: 1.0 is fully opaque. [ObservableProperty] private double _opacity = 0.9; /// The fill of the popup. The opacity is set by . [ObservableProperty] private Color _backgroundColor = Color.FromArgb(0xFF, 0x20, 0x20, 0x20); /// The colour of the layout name in the popup. [ObservableProperty] private Color _foregroundColor = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF); /// /// Takes the offset and the look of another mode over. What tells the modes apart — /// the side, the place on the monitor — is copied by the mode itself. /// protected void CopyLookFrom(PopupModeSettings other) { Offset = other.Offset; FontSize = other.FontSize; Opacity = other.Opacity; BackgroundColor = other.BackgroundColor; ForegroundColor = other.ForegroundColor; } } /// The mode that puts the popup next to the mouse cursor. public sealed partial class CursorModeSettings : PopupModeSettings { /// The corner or the side of the cursor the popup is put on. [ObservableProperty] private AnchorSide _side = AnchorSide.BottomRight; /// Takes the values of another mode of the same kind over. public void CopyFrom(CursorModeSettings other) { CopyLookFrom(other); Side = other.Side; } } /// /// The mode that puts the popup next to the caret of the active input field. /// public sealed partial class CaretModeSettings : PopupModeSettings { /// The side of the caret the popup is put on: only left or right. [ObservableProperty] private CaretSide _side = CaretSide.Right; /// /// The side as the layout arithmetic wants it. Both of them line the popup up with /// the caret rather than putting it above or below. /// [JsonIgnore] public AnchorSide Anchor => Side == CaretSide.Left ? AnchorSide.Left : AnchorSide.Right; /// Takes the values of another mode of the same kind over. public void CopyFrom(CaretModeSettings other) { CopyLookFrom(other); Side = other.Side; } // The side decides what the popup is lined up against, so it is a change of that too partial void OnSideChanged(CaretSide value) => OnPropertyChanged(nameof(Anchor)); } /// /// The mode that puts the popup at a fixed place of the monitor holding the active /// window. Here is the distance from the edge /// of the monitor. /// /// /// In the middle of the monitor there is no edge to keep a distance from, so the offset /// is zero there and the settings window does not offer it. Choosing the middle puts it /// back to zero rather than remembering it for later: a setting that is not shown must /// not be one that still applies. /// public sealed partial class FixedPointModeSettings : PopupModeSettings { /// The place on the monitor the popup is put at. [ObservableProperty] private ScreenPosition _position = ScreenPosition.Center; public FixedPointModeSettings() { // The middle is the default, and it has no edge to stand off from Offset = 0; } /// Whether the popup is put at an edge of the monitor rather than in its middle. [JsonIgnore] public bool IsAtAnEdge => Position != ScreenPosition.Center; /// Takes the values of another mode of the same kind over. public void CopyFrom(FixedPointModeSettings other) { CopyLookFrom(other); Position = other.Position; } partial void OnPositionChanged(ScreenPosition value) { OnPropertyChanged(nameof(IsAtAnEdge)); if (value == ScreenPosition.Center) { Offset = 0; } } }