Zum Inhalt
Kuonen.UI

Mauszeiger

Jedes Element hat einen Cursor; der Host zeigt ihn mit den Zeigern der Plattform (Windows, Linux X11 und Wayland, macOS, Browser als CSS-Cursor). Cursor.AusBild macht aus einem Bild einen eigenen Zeiger mit Hotspot (1 bis 256 Pixel je Seite). Mit ForceCursor gilt der Zeiger eines Elements auch über seinen Kindern, die einen eigenen hätten – wie in WPF.

Vorführung: MauszeigerVorführung: Mauszeiger

KXAML

ZeigerSeite.kxaml
<StackPanel xmlns="https://ui.kuonen.org/kxaml"
            xmlns:x="https://ui.kuonen.org/kxaml/x"
            x:Class="Kuonen.UI.Galerie.Seiten.ZeigerVorfuehrung"
            Spacing="12">
  <TextBlock Text="Zeiger je Element" FontWeight="SemiBold" />
  <WrapPanel x:Name="Felder" />
  <TextBlock Text="Zeiger erzwingen" FontWeight="SemiBold" />
  <CheckBox x:Name="Erzwingen" Content="ForceCursor (Hand auch über dem Textfeld)" Click="Erzwingen_Click" />
  <Border x:Name="Rahmen" Padding="12" BorderThickness="1" BorderBrush="#A0A0A0">
    <TextBox Width="240" Text="Text mit eigenem Zeiger" />
  </Border>
</StackPanel>
ZeigerSeite.kxaml.cs
using Kuonen.UI;
using Kuonen.UI.Eingabe;
using Kuonen.UI.Ereignisse;

namespace Kuonen.UI.Galerie.Seiten;

public sealed partial class ZeigerSeite
{
    protected override Func<UIElement>? KxamlFabrik => () => new ZeigerVorfuehrung();
}

/// <summary>KXAML-Fassung der Zeiger-Seite: der Baum steht in ZeigerSeite.kxaml; die Felder und Zeiger setzt der Code-Behind.</summary>
public sealed partial class ZeigerVorfuehrung
{
    public ZeigerVorfuehrung()
    {
        InitializeComponent();
        ZeigerSeite.Fuelle(Felder);
        Rahmen.Cursor = Cursors.Hand;
    }

    private void Erzwingen_Click(object sender, RoutedEventArgs e) => Rahmen.ForceCursor = Erzwingen.IsChecked == true;
}

C#

ZeigerSeite.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Eingabe;
using Kuonen.UI.Layout;
using Kuonen.UI.Media;

namespace Kuonen.UI.Galerie.Seiten;

/// <summary>Galerie-Seite: Mauszeiger je Element, eigene Zeigerbilder und ForceCursor (Plan 6h).</summary>
public sealed partial class ZeigerSeite : GalerieSeite
{
    public override string Titel => "Mauszeiger";

    public override string Beschreibung =>
        "Jedes Element hat einen Cursor; der Host zeigt ihn mit den Zeigern der Plattform (Windows, Linux X11 und Wayland, macOS, Browser als "
        + "CSS-Cursor). Cursor.AusBild macht aus einem Bild einen eigenen Zeiger mit Hotspot (1 bis 256 Pixel je Seite). Mit ForceCursor gilt der "
        + "Zeiger eines Elements auch über seinen Kindern, die einen eigenen hätten – wie in WPF.";

    public override string Stichworte => "Cursor Mauszeiger Zeiger Cursors AusBild Hotspot ForceCursor Hand IBeam Kreuz";

    public override string Datei => nameof(ZeigerSeite);

    private static Cursor? _eigener;

    /// <summary>Ein eigener Zeiger: Zielscheibe (32 × 32) mit Hotspot in der Mitte.</summary>
    public static Cursor Eigener => _eigener ??= Cursor.AusBild(Zielscheibe(), 16, 16);

    private static BitmapSource Zielscheibe()
    {
        const int n = 32;
        var pixel = new uint[n * n];
        for (int y = 0; y < n; y++)
            for (int x = 0; x < n; x++)
            {
                double dx = x + 0.5 - n / 2.0, dy = y + 0.5 - n / 2.0, r = Math.Sqrt(dx * dx + dy * dy);
                if (r <= 3 || r is >= 9 and <= 11) pixel[y * n + x] = 0xFFC42B1Cu;
                else if (r is >= 14 and <= 15) pixel[y * n + x] = 0xFFFFFFFFu;
            }
        return BitmapSource.Create(n, n, pixel);
    }

    /// <summary>Die Namen und Zeiger der Felder in Reihenfolge.</summary>
    public static (string Name, Cursor Zeiger)[] Zeiger() =>
    [
        ("Pfeil", Cursors.Arrow), ("Hand", Cursors.Hand), ("Text", Cursors.IBeam), ("Kreuz", Cursors.Cross), ("Warten", Cursors.Wait),
        ("Verschieben", Cursors.SizeAll), ("Verboten", Cursors.No), ("Hilfe", Cursors.Help), ("Eigenes Bild", Eigener),
    ];

    /// <summary>Füllt <paramref name="felder"/> mit einem Feld je Zeiger.</summary>
    internal static void Fuelle(Panel felder)
    {
        foreach (var (name, zeiger) in Zeiger())
            felder.Children.Add(new Border
            {
                Width = 120, Height = 56, BorderThickness = new Thickness(1), BorderBrush = new SolidColorBrush(Color.FromRgb(160, 160, 160)),
                CornerRadius = new CornerRadius(4), Margin = new Thickness(0, 0, 8, 8), Cursor = zeiger,
                Child = new TextBlock { Text = name, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center },
            });
    }

    public override UIElement Baue()
    {
        var felder = new WrapPanel();
        Fuelle(felder);
        var rahmen = new Border { Padding = new Thickness(12), BorderThickness = new Thickness(1), BorderBrush = new SolidColorBrush(Color.FromRgb(160, 160, 160)),
            Cursor = Cursors.Hand, Child = new TextBox { Width = 240, Text = "Text mit eigenem Zeiger" } };
        var erzwingen = new CheckBox { Content = "ForceCursor (Hand auch über dem Textfeld)" };
        erzwingen.Click += (_, _) => rahmen.ForceCursor = erzwingen.IsChecked == true;
        var seite = new StackPanel { Spacing = 12 };
        seite.Children.Add(new TextBlock { Text = "Zeiger je Element", FontWeight = 600 });
        seite.Children.Add(felder);
        seite.Children.Add(new TextBlock { Text = "Zeiger erzwingen", FontWeight = 600 });
        seite.Children.Add(erzwingen);
        seite.Children.Add(rahmen);
        return seite;
    }
}