Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -827,9 +827,9 @@ When adding a **new** example project in [Standard-Toolkit-Demos](https://github

#### What to capture

- **PNG** for a static default look of the example (typical theme is enough; do not shoot every theme).
- **GIF** when motion is the point (toggle, toast, menu open, fade, scrollbar interaction), matching neighbours such as `KryptonToggleSwitchExample.gif` or `ToastNotificationExample.gif`.
- Prefer PrintWindow / HWND capture of the example form (same idea as Toolkit **UI Screenshots / GIFs**); verify the image shows the demo, not the IDE.
- **PNG** for a static default look of the example in **both** a light and a dark builtin theme (same baseline as Toolkit **UI Screenshots / GIFs**: `Microsoft365Blue` and `Microsoft365BlackDarkMode`). Do not shoot every theme.
- Prefer filenames that keep the pair obvious (`example-light.png` / `example-dark.png`, or `Example-light.png` / `Example-dark.png`). When the category README historically shows a single image, embed both (two image lines under the heading).
- **GIF** when motion is the point (toggle, toast, menu open, fade, scrollbar interaction), matching neighbours such as `KryptonToggleSwitchExample.gif` or `ToastNotificationExample.gif`. Capture light and dark when the motion is theme-sensitive; one GIF is enough when motion is geometry-only.

#### Where to save the file

Expand Down Expand Up @@ -926,31 +926,36 @@ When a change is **user-visible**, capture stills (and a short GIF when motion i

### What to capture

- Enough to show the change: typical default look plus the distinctive demo state (for example contrast/override, or before/after).
- Do not capture every theme variant unless the bug or feature is family-specific; then include the affected families.
- **PNG** for stills (colour, layout, chrome, default vs override).
- **Short GIF** when motion is the point (drag, tear-out/remerge, animation, slide, flicker). Do not GIF a static colour or layout change, and do not substitute a single still when the defect is motion.
- Enough to show the change: the distinctive demo state (for example contrast/override, or before/after), captured in **both a light and a dark** builtin theme.
- Baseline pair (use these unless the bug or feature is family-specific):
- Light: `PaletteMode.Microsoft365Blue`
- Dark: `PaletteMode.Microsoft365BlackDarkMode`
- Do **not** shoot every theme. When the defect or feature is family-specific, add the affected families **in addition to** the light/dark pair (or replace the baseline pair when that family has no light/dark equivalents and note it in the PR description).
- **PNG** for stills (colour, layout, chrome, light vs dark, default vs override).
- **Short GIF** when motion is the point (drag, tear-out/remerge, animation, slide, flicker). Capture the motion once per light and dark baseline when the motion is theme-sensitive (colours, chrome, contrast); one GIF is enough when motion is geometry-only. Do not GIF a static colour or layout change, and do not substitute a single still when the defect is motion.

### How

1. Build Debug TestForm if binaries are stale: `dotnet build ".\Source\Krypton Components\TestForm\TestForm.csproj" -c Debug`.
2. Host the relevant demo with PowerShell `-STA`. Reuse a `Start-*Host.ps1`, or instantiate the form in-process (pattern: `Scripts/UnitTests/Invoke-RadialMenuScreenshot.ps1`). Place the form on the primary working area (`StartPosition = Manual`, e.g. `(80, 80)`); do not leave it off-screen or minimized.
3. Before capture: `Show`, set `TopMost = $true`, `Activate`, `BringToFront`, `SetForegroundWindow` (via `Initialize-UnitTestNativeInput` / `UnitTestNative`), `Application.DoEvents()`, then a short settle sleep so paint completes.
4. **Capture the HWND, not whatever is on top of the desktop.** Dot-source `Scripts/UnitTests/UnitTestCommon.ps1` and call `Save-UnitTestWindowPng -Form $form -Path $OutputPath`. That helper uses `PrintWindow` with `PW_RENDERFULLCONTENT`, so Cursor / Visual Studio covering the same screen region cannot appear in the PNG. Use `-InflateX` / `-InflateY` only when a popup must be included outside the form bounds (then the helper falls back to TopMost + foreground + `CopyFromScreen`). Do **not** hand-roll bare `Graphics.CopyFromScreen($form.Bounds…)` after only `Activate` — that is the usual “wrong window” failure.
5. **Read the PNG or GIF in the session** (image Read tool) and confirm it shows the demo chrome/content, not the IDE, terminal, or another app. If it is wrong, fix foreground/`Save-UnitTestWindowPng` and recapture; do not embed a bad shot.
6. If the capture is reusable, keep the script under `Scripts/UnitTests/` with `# UnitTest-CI: exclude` and a README row (see **Unit Test Scripts**). Prefer STA `-File` scripts that call `Save-UnitTestWindowPng` over a long `powershell -Command { … }` one-liner (see **Recent Tooling Mistakes To Avoid**).
3. For each of the light and dark baseline modes (and any extra family-specific modes): set `KryptonManager.GlobalPaletteMode` (or a sited `KryptonManager` instance’s `GlobalPaletteMode`) **before** show, or set it and refresh/recreate the form so chrome and content pick up the palette. Pattern: `Scripts/UnitTests/Invoke-DarkModeThemeScreenshots.ps1` / `Invoke-ToolStripTextContrastScreenshot.ps1`. Load `Krypton.Themes.dll` when the mode lives in the Themes assembly.
4. Before capture: `Show`, set `TopMost = $true`, `Activate`, `BringToFront`, `SetForegroundWindow` (via `Initialize-UnitTestNativeInput` / `UnitTestNative`), `Application.DoEvents()`, then a short settle sleep so paint completes.
5. **Capture the HWND, not whatever is on top of the desktop.** Dot-source `Scripts/UnitTests/UnitTestCommon.ps1` and call `Save-UnitTestWindowPng -Form $form -Path $OutputPath`. That helper uses `PrintWindow` with `PW_RENDERFULLCONTENT`, so Cursor / Visual Studio covering the same screen region cannot appear in the PNG. Use `-InflateX` / `-InflateY` only when a popup must be included outside the form bounds (then the helper falls back to TopMost + foreground + `CopyFromScreen`). Do **not** hand-roll bare `Graphics.CopyFromScreen($form.Bounds…)` after only `Activate` — that is the usual “wrong window” failure.
6. **Read each PNG or GIF in the session** (image Read tool) and confirm it shows the demo chrome/content in the intended light or dark palette, not the IDE, terminal, or another app. If it is wrong, fix foreground/`Save-UnitTestWindowPng` / palette apply and recapture; do not embed a bad shot.
7. If the capture is reusable, keep the script under `Scripts/UnitTests/` with `# UnitTest-CI: exclude` and a README row (see **Unit Test Scripts**). Prefer STA `-File` scripts that call `Save-UnitTestWindowPng` over a long `powershell -Command { … }` one-liner (see **Recent Tooling Mistakes To Avoid**).

**GIF (motion only):** same host, STA, on-screen, TopMost/foreground, and verify-the-frames rules as PNG. Prefer per-frame `Save-UnitTestWindowPng` / `PrintWindow` when only the form is needed. Use `CopyFromScreen` on a timer only when the motion leaves the HWND (drag ghost, tear-out). Encode to an animated GIF and save next to the description. Prefer `ffmpeg` or ImageMagick `magick` if on PATH; otherwise assemble frames with WPF `GifBitmapEncoder` (`Add-Type -AssemblyName PresentationCore`). Keep it to a few seconds, cropped, looping. If no encoder is available, capture labelled stills (`-before.png`, `-during.png`, `-after.png`) instead of skipping — do not invent a GIF.
**GIF (motion only):** same host, STA, on-screen, TopMost/foreground, light/dark (when theme-sensitive), and verify-the-frames rules as PNG. Prefer per-frame `Save-UnitTestWindowPng` / `PrintWindow` when only the form is needed. Use `CopyFromScreen` on a timer only when the motion leaves the HWND (drag ghost, tear-out). Encode to an animated GIF and save next to the description. Prefer `ffmpeg` or ImageMagick `magick` if on PATH; otherwise assemble frames with WPF `GifBitmapEncoder` (`Add-Type -AssemblyName PresentationCore`). Keep it to a few seconds, cropped, looping. If no encoder is available, capture labelled stills (`-before.png`, `-during.png`, `-after.png`) instead of skipping — do not invent a GIF.

### Where

- Save reviewer shots as `Documents/PR/<issue-or-branch>-<short-title>-<state>.png` or `.gif` next to the PR description (for example `1100-scheme-strip-text-default.png`, `925-caption-tab-remerge.gif`).
- Save reviewer shots as `Documents/PR/<issue-or-branch>-<short-title>-<state>-light.png` and `…-dark.png` (or `.gif`) next to the PR description (for example `1100-scheme-strip-text-default-light.png`, `1100-scheme-strip-text-default-dark.png`, `925-caption-tab-remerge-light.gif`). Use a clear `<state>` when there are multiple scenarios; keep `-light` / `-dark` as the theme suffix.
- These files are **local**, like the PR description file: do **not** stage, commit, or push them. Do **not** leave the only copy under `Bin/`.
- Embed in `Documents/PR/<file>.md` with relative markdown images and a one-line caption stating the state shown:
- Embed in `Documents/PR/<file>.md` with relative markdown images and a one-line caption stating the state and theme:

```markdown
![Default builtin theme](./1100-scheme-strip-text-default.png)
![Caption tab remerge](./925-caption-tab-remerge.gif)
![Default look (Microsoft 365 Blue)](./1100-scheme-strip-text-default-light.png)
![Default look (Microsoft 365 Black Dark Mode)](./1100-scheme-strip-text-default-dark.png)
![Caption tab remerge (light)](./925-caption-tab-remerge-light.gif)
```

- Do **not** upload, attach, or host these files on the GitHub pull request (no `user-attachments` URLs, no drag-and-drop onto the PR, no `gh` image attach). GitHub will not display local relative paths; that is intended.
Expand All @@ -959,8 +964,9 @@ When a change is **user-visible**, capture stills (and a short GIF when motion i
### Do not

- Skip screenshots or GIFs for UI work, or leave the template placeholder.
- Ship only a light (or only a dark) still when both can be captured — the light/dark pair is required for UI-visible work.
- Capture with bare `CopyFromScreen` after only `Show`/`Activate` (IDE/Cursor occlusion → wrong window).
- Embed a PNG/GIF without reading it in-session to confirm it shows the demo.
- Embed a PNG/GIF without reading it in-session to confirm it shows the demo in the intended palette.
- Commit PNGs, GIFs, or `Bin/` capture output in the Standard-Toolkit pull request.
- Upload or attach screenshot or GIF files to the GitHub pull request.
- Invent or draw substitute images. If capture is impossible (no interactive desktop), say so in **Validation** instead of faking a shot.
Expand Down
2 changes: 2 additions & 0 deletions Documents/Changelog/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@

## 2027-11-xx - Build 2711 (V120 Nightly) - November 2027

- Implemented [#4656](https://github.com/Krypton-Suite/Standard-Toolkit/issues/4656), Analogue gauges in `Krypton.Gauges`: circular needle, linear bar, rectangular (solid or segmented), thermometer (bulb + tube), and ampere meter (square panel, AC or DC center-zero) indicators that share value/range/palette settings, optional animation, and optional interactive input.
- To use, you will need to download the [Krypton.Standard.Toolkit](https://www.nuget.org/packages/Krypton.Standard.Toolkit) NuGet package, as this control is part of the `Krypton.Gauges` assembly. `Krypton.Gauges` is not a separate package.
- Implemented [#4218](https://github.com/Krypton-Suite/Standard-Toolkit/issues/4218), Step timeline (`KryptonTimeline`) and date timeline (`KryptonDateTimeline`) in the `Krypton.Charting` assembly.
- `Style` is `Chart` (row bars and milestone diamonds) or `Vis` (stacked boxes above a bottom axis, in the style of vis.js Timeline). A single date in `Vis` is a box with a stem to the axis. Overlapping items stack. `Row` applies to `Chart` only.
- Drag a step to reorder it. Drag a date item to move its dates; Chart style also changes the row. Dragging a date item turns auto range off, the same as panning. Set `AllowMove` to false to keep items fixed.
Expand Down
5 changes: 3 additions & 2 deletions Documents/PR/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ Delete any section that does not apply.

## Screenshots / GIFs

<!-- Capture per AGENTS.md UI Screenshots / GIFs. Use Save-UnitTestWindowPng (PrintWindow) from Scripts/UnitTests/UnitTestCommon.ps1 — do not bare CopyFromScreen after only Activate (IDE occlusion). Save PNG or GIF files next to this file as Documents/PR/<issue>-<short-title>-<state>.png or .gif. Embed relative images below for the local draft. Read each image in-session to confirm it shows the demo. Do not upload or attach them to the GitHub pull request. Remove this section only when there is no UI change; do not leave this placeholder. -->
<!-- Capture per AGENTS.md UI Screenshots / GIFs. Use Save-UnitTestWindowPng (PrintWindow) from Scripts/UnitTests/UnitTestCommon.ps1 — do not bare CopyFromScreen after only Activate (IDE occlusion). Capture both light (Microsoft365Blue) and dark (Microsoft365BlackDarkMode) unless Validation explains why one side cannot be shot. Save as Documents/PR/<issue>-<short-title>-<state>-light.png and …-dark.png (or .gif). Embed relative images below for the local draft. Read each image in-session to confirm it shows the demo in the intended palette. Do not upload or attach them to the GitHub pull request. Remove this section only when there is no UI change; do not leave this placeholder. -->

![<state>](./<issue>-<short-title>-<state>.png)
![<state> (light)](./<issue>-<short-title>-<state>-light.png)
![<state> (dark)](./<issue>-<short-title>-<state>-dark.png)

## Changelog

Expand Down
97 changes: 97 additions & 0 deletions Scripts/UnitTests/Invoke-AnalogueGaugeScreenshot.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<#
.SYNOPSIS
Hosts KryptonAnalogueGaugeDemo and captures light/dark stills for the local PR draft.

.DESCRIPTION
Loads the Debug TestForm build in-process (STA), shows the analogue gauge demo under
Microsoft 365 Blue and Microsoft 365 Black Dark Mode, and writes
Documents/PR/4656-analogue-gauges-default-light.png and …-dark.png via PrintWindow.
Loads Krypton.Themes.dll for the dark extra palette. Local PR assets only; do not commit them.

# UnitTest-CI: exclude

.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -STA -File .\Scripts\UnitTests\Invoke-AnalogueGaugeScreenshot.ps1
#>
[CmdletBinding()]
param(
[string]$Configuration = 'Debug',
[string]$TargetFramework = 'net472',
[string]$BinDir,
[string]$OutputDirectory
)

$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'UnitTestCommon.ps1')

$repoRoot = Get-UnitTestRepoRoot
$bin = Get-UnitTestBinDir -RepoRoot $repoRoot -Configuration $Configuration -TargetFramework $TargetFramework -BinDir $BinDir
if (-not $OutputDirectory) {
$OutputDirectory = Join-Path $repoRoot 'Documents\PR'
}
if (-not (Test-Path -LiteralPath $OutputDirectory)) {
New-Item -ItemType Directory -Path $OutputDirectory | Out-Null
}

Register-UnitTestAssemblyResolver -BinDir $bin
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms
Initialize-UnitTestNativeInput
[void][System.Reflection.Assembly]::LoadFrom((Join-Path $bin 'Krypton.Toolkit.dll'))
$themesDll = Join-Path $bin 'Krypton.Themes.dll'
if (Test-Path -LiteralPath $themesDll) {
[void][System.Reflection.Assembly]::LoadFrom($themesDll)
}
[void][System.Reflection.Assembly]::LoadFrom((Join-Path $bin 'Krypton.Gauges.dll'))
$testFormPath = Join-Path $bin 'TestForm.dll'
if (-not (Test-Path -LiteralPath $testFormPath)) {
$testFormPath = Join-Path $bin 'TestForm.exe'
}

$asm = [System.Reflection.Assembly]::LoadFrom($testFormPath)
[System.Windows.Forms.Application]::EnableVisualStyles()
$formType = $asm.GetType('TestForm.KryptonAnalogueGaugeDemo')
if (-not $formType) {
throw 'Type TestForm.KryptonAnalogueGaugeDemo was not found in TestForm.'
}

$shots = @(
@{ Mode = [Krypton.Toolkit.PaletteMode]::Microsoft365Blue; File = '4656-analogue-gauges-default-light.png' }
@{ Mode = [Krypton.Toolkit.PaletteMode]::Microsoft365BlackDarkMode; File = '4656-analogue-gauges-default-dark.png' }
)

$manager = New-Object Krypton.Toolkit.KryptonManager
try {
foreach ($shot in $shots) {
$manager.GlobalPaletteMode = $shot.Mode
$form = [System.Activator]::CreateInstance($formType)
try {
$manager.GlobalPaletteMode = $shot.Mode
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
$form.Location = New-Object System.Drawing.Point 40, 40
$form.TopMost = $true
$form.Show()
$form.Refresh()
$form.Activate()
$form.BringToFront()
[void][UnitTestNative]::SetForegroundWindow($form.Handle)
[System.Windows.Forms.Application]::DoEvents()
Start-Sleep -Milliseconds 500
[System.Windows.Forms.Application]::DoEvents()

$outPath = Join-Path $OutputDirectory $shot.File
Save-UnitTestWindowPng -Form $form -Path $outPath -SettleMs 250
Write-Output $outPath
}
finally {
if ($form -ne $null) {
$form.Close()
$form.Dispose()
}
[System.Windows.Forms.Application]::DoEvents()
}
}
}
finally {
$manager.Dispose()
}
9 changes: 9 additions & 0 deletions Scripts/UnitTests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ powershell -NoProfile -ExecutionPolicy Bypass -STA -File .\Scripts\UnitTests\Inv

Writes `Documents/PR/4655-vu-meter-*.png` and `4655-vu-meter.gif` (local PR assets; do not commit). That capture leaves Play music off.

### Analogue gauges (#4656)

```powershell
dotnet build ".\Source\Krypton Components\TestForm\TestForm.csproj" -c Debug -f net472
powershell -NoProfile -ExecutionPolicy Bypass -STA -File .\Scripts\UnitTests\Invoke-AnalogueGaugeScreenshot.ps1
```

Writes `Documents/PR/4656-analogue-gauges-default-light.png` and `…-dark.png` (Microsoft 365 Blue / Black Dark Mode; local PR assets; do not commit).

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -STA -File .\Scripts\UnitTests\UnitTest-VuMeterMusic.ps1
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ foreach ($name in @(
'Krypton.Toolkit.Utilities.dll',
'Krypton.Navigator.Utilities.dll',
'Krypton.Charting.dll',
'Krypton.Gauges.dll',
'Krypton.Gauges.dll'
)) {
$path = Join-Path $bin $name
if (Test-Path -LiteralPath $path) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading