Files
kniip/.cursor/plans/emoji_stempel-tool_8dbf6487.plan.md
T
2026-11-07 12:19:45 +01:00

109 lines
7.5 KiB
Markdown

---
name: Emoji stempel-tool
overview: "Voeg een Emoji-gereedschap toe aan de werkbalk van de editor. Je plaatst er een Twemoji-stempel mee op het knipsel. De kiezer werkt zoals emoji-mart: alle emoji, categorie-tabs, zoeken in het Nederlands en Engels, huidskleur en \"recent gebruikt\"."
todos:
- id: assets
content: Script tools/fetch-emoji.ps1 en Assets/Emoji (emoji.json, twemoji-64.png, cldr-nl.json) toevoegen als Resource in csproj
status: completed
- id: catalog
content: "EmojiCatalog: json/CLDR parsen, sheet croppen met cache, categorieën, zoeken, huidskleurvarianten"
status: completed
- id: model
content: ToolKind.Emoji, SkinTone, StyleSet.Emoji/SkinTone, StyleProps.Emoji, DefaultFor/PropsFor
status: completed
- id: annotation
content: EmojiAnnotation (Center, Bounds, Render met DrawImage en schaduw, Move)
status: completed
- id: picker
content: "EmojiPickerButton UserControl: zoeken, categorie-tabs, gevirtualiseerd raster, preview, huidskleur, recent"
status: completed
- id: editor
content: "EditorWindow: toolbarknop, EmojiGroup in PropsPanel, CreateAnnotation, stempel-case, sneltoets J, hint, stijl toepassen op selectie"
status: completed
- id: settings
content: AppSettings.RecentEmoji en controle van ToolbarPanel-volgorde voor het nieuwe item
status: completed
- id: verify
content: "Builden en testen: plaatsen, wisselen, huidskleur, zoeken NL/EN, export/kopiëren, undo"
status: completed
isProject: false
---
# Emoji-gereedschap met Twemoji-kiezer
## Aanpak
WPF tekent Segoe UI Emoji alleen zwart-wit. Daarom leveren we de **Twemoji-set van emoji-datasource** mee. Dat is dezelfde bron die emoji-mart gebruikt voor zijn "twitter"-set:
- `emoji.json`: namen, categorieën, sorteervolgorde, huidskleurvarianten en de positie van elke emoji in het sheet.
- `sheet_twitter_64.png`: één spritesheet waarin elke emoji 64 px groot is, met 1 px opvulling.
- CLDR `annotations/nl.json`: Nederlandse namen en trefwoorden, zodat je ook in het Nederlands kunt zoeken.
Een emoji wordt een stempel-annotatie, net als `StepAnnotation` en `CursorAnnotation`. Je klikt om hem te plaatsen, en daarna kun je hem verplaatsen, ongedaan maken en exporteren via de bestaande renderpijplijn.
```mermaid
flowchart LR
Assets["Assets/Emoji: json + sheet + nl"] --> Catalog[EmojiCatalog]
Catalog --> Picker[EmojiPickerButton]
Catalog --> Anno[EmojiAnnotation]
Picker -->|"gekozen emoji + huidskleur"| Style[StyleSet.Emoji]
Style --> Anno
Anno --> Surface[AnnotationSurface en ImageExporter]
```
## Bestanden
**Assets (nieuw):** `src/Kniip/Assets/Emoji/` met `emoji.json`, `twemoji-64.png` en `cldr-nl.json`. Deze komen in [src/Kniip/Kniip.csproj](src/Kniip/Kniip.csproj) als `<Resource Include="Assets\Emoji\*" />`. De bestanden worden eenmalig gedownload met een klein script, `tools/fetch-emoji.ps1`, dat vaste versies van emoji-datasource-twitter (15.x) en cldr-json binnenhaalt. De bestanden zelf worden ingecheckt, zodat de build offline blijft werken.
**`src/Kniip/Editor/Emoji/EmojiCatalog.cs` (nieuw)**
- Leest bij het eerste gebruik de json en het sheet (lazy, statisch). Emoji die niet in de Twitter-set zitten (`has_img_twitter`) en verouderde varianten (`obsoleted_by`) worden overgeslagen.
- Het model is `EmojiInfo` met deze velden: `Unified`, `Char`, `Name`, `ShortNames`, `Category`, `SortOrder`, `Keywords` (Engels plus Nederlands) en `SkinVariations` (een dictionary van toon naar sheetpositie).
- `ImageSource Get(string unified, SkinTone tone)`: een `CroppedBitmap` uit het sheet, in een cache en frozen.
- `IEnumerable<EmojiInfo> Search(string query)` zoekt in namen, shortcodes en trefwoorden, zonder op hoofdletters te letten.
- Categorieën in de volgorde van emoji-mart, met Nederlandse labels: Recent, Smileys en emotie, Mensen, Dieren en natuur, Eten en drinken, Activiteiten, Reizen en plaatsen, Objecten, Symbolen, Vlaggen.
**`src/Kniip/Editor/Annotations/EmojiAnnotation.cs` (nieuw)**
- Een stempel zoals `CursorAnnotation`: `Center` als `Point`, de grootte komt uit `Style.Size`, en `Bounds` is een vierkant rond `Center`.
- `Render` gebruikt `dc.DrawImage(EmojiCatalog.Get(Style.Emoji, Style.SkinTone), Bounds)`. Bij `Style.Shadow` komt er een schaduw onder, op dezelfde manier als in `RenderHelpers.DrawWithShadow`, maar dan voor een afbeelding (met een `DrawingGroup` en een `DropShadowEffect`-equivalent, of een offset-kopie met lage opaciteit).
- Er gebruikt `RenderOptions.SetBitmapScalingMode(HighQuality)` voor nette schaling.
**[src/Kniip/Editor/StyleSet.cs](src/Kniip/Editor/StyleSet.cs)**
- `ToolKind.Emoji` komt achter `Cursor`.
- Nieuwe enum `SkinTone { Default, Light, MediumLight, Medium, MediumDark, Dark }`.
- `StyleSet` krijgt `string Emoji = "1f44d"` (duim omhoog) en `SkinTone SkinTone`.
- Nieuwe `StyleProps.Emoji = 8192`.
- `DefaultFor`: `ToolKind.Emoji => new StyleSet { Size = 48, Shadow = false }`.
- `PropsFor`: `ToolKind.Emoji => StyleProps.Emoji | StyleProps.Size | StyleProps.Shadow`.
**`src/Kniip/Editor/EmojiPickerButton.xaml(.cs)` (nieuw)**
Dit volgt hetzelfde patroon als [ColorPickerButton](src/Kniip/Editor/ColorPickerButton.xaml.cs): een `ToggleButton` met een `Popup`, en het event `EmojiChanged`. De popup is ongeveer 350x420 en bevat:
- Een zoekveld bovenaan, dat focus krijgt zodra de popup opent.
- Een rij met categorie-tabs. Een klik scrollt naar de bijbehorende sectie.
- Een gevirtualiseerd raster: `ItemsControl` met `VirtualizingStackPanel` en per categorie een rij van negen emoji van 32 px.
- Onderaan een preview met de naam en `:shortcode:` van de emoji waar je met de muis boven hangt, plus een huidskleurkiezer met zes bolletjes.
- Na een keuze sluit de popup.
**[src/Kniip/Settings/AppSettings.cs](src/Kniip/Settings/AppSettings.cs)**
- Nieuwe instelling `List<string> RecentEmoji`, met maximaal 18 items en de laatst gekozen emoji vooraan. Deze lijst vult de tab "Recent".
**[src/Kniip/Editor/EditorWindow.xaml](src/Kniip/Editor/EditorWindow.xaml)**
- Een nieuwe `RadioButton Tag="Emoji"` achter `Cursor`, met `ItemId="Emoji"` en `Title="Emoji"`, tooltip "Emoji plaatsen (J)" en als icoon het Segoe Fluent-glyph `&#xE76E;` (Emoji2).
- In `PropsPanel` komt een `EmojiGroup` met `EmojiPickerButton`. `SizeGroup` en `ShadowGroup` worden hergebruikt.
**[src/Kniip/Editor/EditorWindow.xaml.cs](src/Kniip/Editor/EditorWindow.xaml.cs)**
- `CreateAnnotation`: `ToolKind.Emoji => new EmojiAnnotation { Center = p, Style = style }`.
- De stempel-case in `Surface_MouseLeftButtonDown` (rond regel 490) krijgt er `case ToolKind.Emoji:` bij.
- `CursorForTool`: `Emoji` gebruikt `Cursors.Hand`.
- Sneltoets `Key.J`: `SelectTool(ToolKind.Emoji)`.
- Hinttekst: "Klik om de gekozen emoji te plaatsen; kies een andere emoji in de balk eronder."
- `UpdatePropsPanel` en het toepassen van stijl op een geselecteerd object krijgen `StyleProps.Emoji` erbij. Zo kun je de emoji van een geplaatste stempel wisselen.
- De grootte-opties voor Emoji: 24, 32, 48, 64, 96 en 128. Boven 64 wordt het bitmap opgeschaald; dat is acceptabel.
**[src/Kniip/Editor/ToolbarPanel.cs](src/Kniip/Editor/ToolbarPanel.cs)**
- Controleren of een opgeslagen `ToolbarOrder` zonder "Emoji" het nieuwe item gewoon op de standaardplek toont. Is dat niet zo, dan wordt dat ingebouwd.
## Aandachtspunten
- De app wordt ongeveer 3 tot 8 MB groter door het sheet en de json. Het sheet wordt pas geladen als je het Emoji-gereedschap voor het eerst gebruikt.
- Twemoji valt onder CC-BY 4.0. De naamsvermelding komt in de README of het Over-scherm.
- Bestaande stijlen in de instellingen blijven compatibel, omdat de nieuwe velden standaardwaarden hebben.