48 Commits
Author SHA1 Message Date
NilsBriggen 098cbf48f5 Install binutils for Linux release packaging
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 14s
CI / build (push) Successful in 1m34s
2026-09-25 00:06:39 +02:00
NilsBriggen 954082a5af Attribute GitHub snapshots to source contributors
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 11s
CI / build (push) Successful in 1m32s
2026-09-24 23:33:52 +02:00
NilsBriggen 579d183f6c Use upstream helper actions for Gitea workflows
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 45s
CI / build (push) Successful in 2m46s
2026-09-24 23:26:37 +02:00
NilsBriggen cfd57ef867 Document ARM64 image pre-pull for Gitea runner
CI / build (push) Failing after 20s
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 12s
2026-09-24 23:24:08 +02:00
NilsBriggen f2a538c50b Install xxd for Gitea test builds
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 1m14s
CI / build (push) Successful in 1m44s
2026-09-24 23:16:58 +02:00
NilsBriggen a357df7b49 Mirror release sources without inherited history
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 21s
CI / build (push) Failing after 1m33s
2026-09-24 23:10:53 +02:00
NilsBriggen fa0864394f Rework Gitea CI and cross-platform release builds
CI / pull_request_policy (push) Skipped
CI / markdown (push) Successful in 53s
CI / build (push) Failing after 2m17s
2026-09-24 22:56:40 +02:00
NilsBriggen 88c2fd34d4 Add missing files
Build / Do we need to build the application? (push) Failing after 4s
Build / Gradle builder (push) Skipped
Code style / Is there any Java source code to lint? (push) Failing after 2s
Code style / Code style linter (push) Skipped
CodeQL / Is there any code to analyze? (push) Successful in 5s
MD Lint / Markdown linter (push) Failing after 9s
Translations / Translations linter (push) Successful in 6s
CodeQL / Analyze (java) (push) Failing after 1m15s
2026-09-24 22:14:52 +02:00
NilsBriggen a4b9e4f770 Cleanup and polish
Build / Do we need to build the application? (push) Failing after 3s
Code style / Is there any Java source code to lint? (push) Failing after 3s
Build / Gradle builder (push) Skipped
Code style / Code style linter (push) Skipped
CodeQL / Is there any code to analyze? (push) Successful in 6s
MD Lint / Markdown linter (push) Failing after 9s
Translations / Translations linter (push) Failing after 8s
CodeQL / Analyze (java) (push) Failing after 1m21s
2026-09-24 22:14:18 +02:00
NilsBriggen 826fb1e05c Finished initial rebuild
Build / Do we need to build the application? (push) Failing after 3s
Code style / Is there any Java source code to lint? (push) Failing after 3s
Build / Gradle builder (push) Skipped
Code style / Code style linter (push) Skipped
CodeQL / Is there any code to analyze? (push) Successful in 7s
MD Lint / Markdown linter (push) Failing after 12s
Translations / Translations linter (push) Failing after 7s
CodeQL / Analyze (java) (push) Failing after 1m27s
2026-09-24 20:11:11 +02:00
NilsBriggenandClaude Opus 5 0a62266649 Fix the interface scale, and gather every colour onto one page
Build / Do we need to build the application? (push) Failing after 3s
Code style / Is there any Java source code to lint? (push) Failing after 3s
Build / Gradle builder (push) Skipped
Code style / Code style linter (push) Skipped
CodeQL / Is there any code to analyze? (push) Successful in 6s
Translations / Translations linter (push) Failing after 6s
MD Lint / Markdown linter (push) Failing after 12s
CodeQL / Analyze (java) (push) Failing after 1m23s
The interface came up far too small on a high-resolution screen. Two causes, and
the first is the more serious:

PrefMonitorDouble and PrefMonitorBoolean wrote a new value to the preference
store and never updated their own cached copy, so a preference only took effect
when the store's listener happened to fire on another thread. For the scale that
was actively wrong: the monitor is constructed with the automatic guess, its
constructor calls set(stored) which wrote the stored value back while leaving
the cache on the guess, and the two then disagreed. Every boolean preference in
the program behaved the same way - set it, read it back, get the old answer.
PrefMonitorInt had this fixed earlier in the round; these two were missed.

Second, the test suite and the development snapshot tool both write the scale
into the developer's real preference store and never put it back, so any run of
`gradlew test` left it pinned at 1.0 - and once a value is stored, the automatic
guess never applies again. Both now restore what was there.

Separately: thirty-five colour controls were split between the Window page and
the Simulation page, two pages with nothing else in common, with three different
reset buttons between them and no grouping on either. Nobody looking for "the
colour of a wire carrying a one" would have guessed which to open. They are now
one Colors page grouped into Canvas, Components, Signal values, Problems and a
folded-away set of Karnaugh covers, with one reset offering both palettes. The
sixteen cover swatches were sixteen fields and ninety-six lines of copy-paste
under a "FIXME: refactor this code"; they are a loop.

Also: the hex editor's controls are a heading rather than a centred strip that
reflowed on every resize and ended in a button closing the window it sat in, and
the toolbar options page has icon buttons instead of five stacked text ones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 16:09:29 +02:00
NilsBriggenandClaude Opus 5 2532c84de2 Credit the changelog entries
Build / Do we need to build the application? (push) Failing after 25s
Code style / Is there any Java source code to lint? (push) Failing after 25s
Build / Gradle builder (push) Skipped
Code style / Code style linter (push) Skipped
CodeQL / Is there any code to analyze? (push) Successful in 6s
Translations / Translations linter (push) Failing after 6s
MD Lint / Markdown linter (push) Failing after 27s
CodeQL / Analyze (java) (push) Failing after 18m9s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 11:55:23 +02:00
NilsBriggenandClaude Opus 5 4e0419fe5d Open the timing diagram and test vectors in the drawer
Simulate > Logging and Simulate > Test Vector each opened a second
operating-system window, with its own menu bar and its own taskbar entry,
positioned under the main window by arithmetic on the raw screen size. Watching
a waveform next to the circuit it describes meant tiling two windows by hand -
and the shell has had a bottom drawer since the window was rebuilt, holding
nothing but the VHDL console.

Both views now hand their contents to that drawer, built the first time they are
asked for. The frames remain as the owners of their listeners and menu wiring;
they are simply never shown. The test view's own "Close Window" button goes with
the move, since a button that closes the panel it is sitting inside reads as a
mistake once the panel has a tab.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 11:54:39 +02:00
NilsBriggenandClaude Opus 5 efb8059f6f Rework the FPGA and SoC windows, and delete a duplicated editor
There were two HdlContentEditors, 310 and 324 lines, differing mostly in which
HdlModel they imported. The vhdl/gui one had no callers anywhere - its only
reference was its own unused factory method - so it is gone rather than merged.

The Synthesize & Download window could not be resized at all, and was five heavy
2px-stroked boxes stacked on top of each other, each with its caption notched
into the outline. Those were the last five such boxes in the program; they are
collapsible sections now. Its Stop button wore the "remove circuit" icon, with
no label and no tooltip.

The board-mapping window pinned itself above every other window on the desktop
until it was closed, and could not be resized to fit the picture it exists to
show. The board editor's empty state was six lines of 20pt bold text positioned
at absolute pixel rows 100 and 200, so it slid off the panel as soon as the
interface scale was raised.

A RISC-V or Nios2 processor draws its registers, interrupts and trace directly
on the circuit canvas, and the attribute that turns that on is set by default.
It was a bright yellow slab with a royal-blue header strip, white cells holding
blue figures, and a magenta program counter - dropped onto a canvas that had
been restyled around it. CpuStyle names those roles against the design system
and keeps a high-contrast branch for printing.

Also: the VHDL editor reserved an empty FlowLayout strip above the code whose
only contribution was its own vertical gap, and the VHDL console printed a
second untranslated heading directly under the drawer tab that already names it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 11:46:34 +02:00
NilsBriggenandClaude Opus 5 84d3f2eb51 Rework the analysis window and give the K-map a dark palette
The Circuit Analysis window was sized by two invisible JPanels wedged into WEST
and NORTH, and its six actions sat in a centred FlowLayout strip that re-wrapped
onto a second row whenever the window narrowed, with no separation between the
one that rewrites the project and the ones that only export. A minimum size says
what the struts were saying, and the actions are grouped at the trailing edge
with Build Circuit as the default.

The Karnaugh map's sixteen cover colours existed in one set only, so dark mode
produced its own by blending each 45% toward white. That pushed the dark end of
the palette together, in the one place where telling covers apart is the whole
point. Each cover now has a colour per theme, registered like every other themed
colour so a colour the user picks still survives a theme switch, and the blend
is gone.

Writing the test for that turned up a collision in the light palette that has
always been there: covers 3 and 6 are a pale pink and a pale peach only 40 apart
in RGB. Cover 6 is now a warm tan, and KarnaughPaletteTest holds both sets to a
minimum separation and the dark set to a minimum lightness.

Also in this window: three scroll panes forced their scrollbar to be visible
whether or not there was anything to scroll, inline validation was raw red in
one tab and uncoloured in another, the CSV import preview was white panels with
black rules whatever the theme, and the simplifier's progress log was a
white-on-black terminal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 11:09:35 +02:00
NilsBriggenandClaude Opus 5 da7997c267 Rebuild Project Options and repair the preferences pages
Project Options was the last stock tabbed window: four tabs in a dialog fixed at
450x300, the fourth of which held a single button. It is now the same page list
the preferences window uses, and "Reset All Settings" is a footer control that
asks before throwing away every setting in the project - it had done it on one
click, with no undo.

Every preferences and options page declares a sentence saying what it is for.
They are written, they are translated into twelve languages, and nothing has
ever rendered any of them. PanelHeader gained a subtitle and both windows pass
it.

Dialogs centred OK and Cancel with glue on both sides and set no default button,
so Enter did nothing. One fix in JDialogOk covers its four users.

The FPGA board error dialog built its own modal out of a grid bag, decided which
icon to draw by comparing the caller's string against the English literal
"Warning", put that same untranslated string in the title bar, and forced itself
always-on-top. It now takes a severity and says it through the shared dialog.

The mouse-mapping page replaced a combination that was already in use without a
word, so the displaced binding was only noticed later by its tool not appearing.
It asks now. The drop target was an etched box indistinguishable from any other
sunken panel, painting its text in hardcoded black and grey.

And the Hotkey page started a 200ms timer, in every open preferences window,
which polled the panel's own width for ever to set a preferred size the layout
can work out by itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 11:01:40 +02:00
NilsBriggenandClaude Opus 5 168c70f85e Show where you are inside a subcircuit
Double-clicking into a subcircuit showed its name in the status bar and nothing
else - exactly what is shown when that circuit is opened on its own. Nothing
said the user was inside one particular instance, three levels down, and the
only way back up was a submenu of the Simulate menu.

The status bar now carries the chain of circuits descended through, each segment
clickable to return to it. The walk up the parent states is the one MenuSimulate
already does to build that submenu.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 10:47:05 +02:00
NilsBriggenandClaude Opus 5 1161a44116 Finish the wording pass and fix what it uncovered
The circuit analysis window was skipped by the earlier pass entirely - its
bundle had not been touched - so it was still titled "Combinational Analysis"
with tabs reading Inputs & Outputs and Minimized, a button reading "Set As
Expression", and two adjacent dropdowns both labelled with a bare noun. It now
reads like the rest of the program, and docs/glossary.md records it.

Three portability and legibility defects came out of the same pass:

The hex editor drew its dump in the proportional interface font. Nothing had
ever called setFont on it, so every hex digit had a different advance width and
the byte columns did not line up - in the one window where lining up is the
whole point. Its address ruler was italic in the same colour as the data, so
there was no boundary before the first byte column.

The generated circuit appearance asked for "Courier 10 Pitch", a face that ships
only on Linux. On Windows and macOS the circuit name and port labels silently
fell back to a proportional font while still being laid out on a monospace
assumption.

Three of the five choices under "Toolbar location" did nothing - the window puts
the toolbar across the top whatever is chosen, and only "hidden" is read - and
the entire "Main canvas location" control had no reader anywhere in the program.
The toolbar control now offers Shown or Hidden, which is what it does.

Also: the Synthesize & Download window's four tabs were English literals rebuilt
by string concatenation in ten places; "Undo VHDL edits" reached the Edit menu
untranslated; the HDL editor never positioned itself and opened in the corner of
the screen; and one Preferences label kept the previous language until restart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 10:42:47 +02:00
NilsBriggenandClaude Opus 5 3c09c9b3ed Add the missing verbs and make the shell answer the keyboard
Renaming a circuit was not possible. LogisimFileActions had add, remove, move
and setMain but no rename, and the only way to do it was to click empty canvas
so the properties panel fell back to the circuit's own properties, then find the
name row - which meant knowing the panel does that. renameCircuit is built on
the same circuit mutation that row used, so it is one undoable step and the
duplicate-name check still applies, and it is on F2 and in the context menu.

The Properties panel and the bottom drawer could be opened but never shut. Their
visibility was modelled in ShellLayout and persisted in LayoutPrefs, and nothing
was wired to either setter, so a wide schematic could not have the column back
and the drawer stayed up once the VHDL simulator had raised it.

The shell was mouse-only. The activity bar - the primary navigation - could not
be focused, tab traversal skipped it, and it was invisible to a screen reader;
it is now focusable, answers Enter and Space, draws its focus, and reports
itself as a button with a name. The editor tabs answer Ctrl+Tab, Ctrl+Shift+Tab
and Ctrl+W and have a Close / Close Others / Close All menu. The Circuits list
answers F2 and Delete. And typing in a filter and pressing Enter did nothing at
all, which is the one thing a filter with a single match left ought to answer.

Circuit Statistics and Export Circuit are in the Circuits panel's menu too.
Exporting a single circuit had been reachable only by switching the component
panel back to the library tree the palette replaced.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 10:36:28 +02:00
NilsBriggenandClaude Opus 5 73a423e07f Retire the 2005 palette from the windows around the canvas
Phase 1 restyled the canvas and Phase 1b the component painters, but the views
beside them were untouched, and several were outright unreadable in the dark
theme.

The worst was in the main toolbar: the tool whose properties were showing got a
solid #FF00FF square filled behind it, which hid the icon it was meant to point
at. It is now tinted the way a palette tile is. Phase 1 had removed the same
magenta from the canvas; this was the other half of it, and Canvas.HALO_COLOR
now has no readers and is gone.

The simulator's three canvas markers were still raw RED, BLUE and MAGENTA, the
last of them carrying the original author's "// fixme". They are now error,
accent and warning from the design system, and collapse to plain ink when
printing, like every other marker. The off-screen-content arrows were painted in
a 36%-alpha navy held in a constant still named TICK_RATE_COLOR.

The timing diagram was the only place left asking for a serif face - italic
Serif for its messages and for the entire time ruler, at a size that ignored the
interface scale. The ruler now uses the monospaced face so its figures line up.
It drew its baseline, ticks and timestamps in black and marked the cursor with a
red hairline and a yellow badge.

The Test Vector table drew every value, heading and rule in black, so on a dark
window it was black on black, and its three status colours were pastels chosen
by colour name rather than by what they mean.

Also: K-map entries in pure Logisim blue, the truth table's hardcoded grey rules
beside an already-themed selection, the timing signal tree's hand-drawn blue
disclosure triangle, a sub-signal drawn as a magenta placeholder positioned with
its x coordinate passed for y, the three hand-painted HDL toolbar icons, the
register watch's Windows-3.x grey bands in bold italic, and a magenta-on-black
debug marker still live in the FPGA report list for any row containing "BUG".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 10:17:10 +02:00
NilsBriggenandClaude Opus 5 746a08d1e3 Fix the property panel's wheel, multi-edit and "various"
Three defects in the mouse-wheel nudge, all of them things the code was already
half-set-up to avoid:

It applied every intermediate value. Spinning a bit width from 1 to 16 left
fifteen entries in the undo history and asked the circuit to re-fit its wires
fifteen times. The two fields that were meant to coalesce the gesture were
captured and then only nulled again. The number now moves in the table as the
wheel turns and is written to the circuit once, when the wheel settles.

It decided whether a value could go below one by asking whether the property's
*translated* display name contained "width". That worked in English and nowhere
else, and it clamped unrelated properties whose German or French name happened
to contain the substring. Bit widths, integer ranges and durations have always
known their own limits privately; BoundedAttribute lets them say so, and
AttrWheelNudge asks.

It discarded the exception from a refused value, twice, so the number snapped
back with no explanation.

Alongside: selecting several rows and setting them in one go only ever worked
for a splitter's bit rows, because the rule asked whether both rows were that
one kind of attribute. The general rule is that two rows can be set together
when they offer the same choices, which is what AttrMultiEdit now asks - so
eight pins' data widths or four gates' facing can be set at once.

And when several selected components disagree about a property, the panel said
nothing at all: an empty cell, indistinguishable from a property with no value
and from one the program failed to read. SelectionAttributes already knew they
disagreed; it now says so, and the panel writes "(various)".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-24 09:42:49 +02:00
NilsBriggenandClaude Opus 5 faa02eb4ca Stop component painters drawing in pure black
Phase 1 restyled the shared drawing code, but a hundred components paint their
own interiors, and twenty-seven of those places set Color.BLACK directly. On a
dark canvas that is black on black: a register's contents, a counter's mode
labels, a probe's reading, a constant's value, the caption on every TTL chip and
the value shown while poking a memory were all invisible.

They now take the theme's component colour, which is what the shared code has
always used. The two appearance shapes set their label colour where they paint
rather than once when they are constructed, so changing theme with the
appearance editor open does not leave the old colour behind.

Components that fill a surface of their own before drawing on it are left alone,
because black is right there: the video component's simulated screen, the
oscilloscope's white trace area, the PLA editor's panel, the outline around a
keypad's coloured keys, and the outline of a TTL package's dark grey body.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 14:03:31 +02:00
NilsBriggenandClaude Opus 5 fbb7c3966f Rewrite the English wording
"Poke", "Toolbox", "Plexers", "Radix" and "Attributes" are words this program
invented or kept from a textbook, and they are the words a long-time user
recognises before they recognise anything else. They are now Interact,
Components, Multiplexers, Number base and Properties. "Add Circuit" is "New
Circuit", because nothing is being added to; "Raise To Top" is "Bring to Front",
because that is what it is called everywhere else; "Can't Undo" is "Nothing to
Undo", because it is not that the program refuses.

docs/glossary.md records every change, so course notes and tutorials written
against an earlier release can be brought up to date without hunting.

Only the English bundle changed and no key was renamed, so the twelve
translations keep working and fall back to English for the text they have not
been given yet.

The danger in a pass like this is renaming a key rather than its value, which is
not a compile error and not an exception: the program shows the key itself where
a sentence should be, and nobody notices until a user does. StringKeyCoverageTest
now checks every lookup in the program against the bundle it will actually read.
It found six that were already broken, among them the LED Bar's name in the
appearance editor and the caption drawn on the Real Time Clock.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 13:53:14 +02:00
NilsBriggenandClaude Opus 5 436bcd4705 Replace the library tree with a component palette
The built-in libraries are organised the way the program is built rather than
the way a circuit is drawn. A pin and a clock were filed under Wiring beside a
splitter; an LED sat beside a keyboard; and "Plexers" is a word most people
meet for the first time here. All of it was presented as a folder tree in which
every one of several hundred rows looked like every other.

The palette shows each component's own symbol -- drawn by the component itself,
the same code that draws it on the canvas -- in groups named after what the
components are for. A component is recognised by its shape long before its name
is read, so the shape is what is shown.

PaletteCatalog, which decides the grouping, is a presentation layer and nothing
else: library identifiers, the structure of std/Builtin and what is written into
a .circ file are untouched, and a library the table says nothing about keeps all
of its components under its own name. That is what makes it safe to ship, and
PaletteCatalogTest holds it to both halves of that promise.

Components can be pinned, and the ones used most recently rise to the top on
their own. The library tree is still one button away, because it is the only
place a library can be unloaded, reloaded or reordered.

The buttons for adding, reordering and removing a circuit have moved to the
Circuits panel, which is what they act on; they had been sitting above the
component library.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 13:48:01 +02:00
NilsBriggenandClaude Opus 5 3b4b028a6a Redraw the circuit canvas
The window was rebuilt last round; what a long-time user still recognised
instantly was everything inside it. The drawing itself had not changed:
two-pixel black rectangles, a magenta rotated ellipse around the haloed
component, white-on-black selection handles, an even field of grey dots.

Component painting looks bespoke -- a hundred files under std/ draw their own
interiors -- but the decisions that make a drawing look the way it does pass
through a handful of places. A new gui/canvas/CanvasStyle gathers them:
GraphicsUtil.switchToWidth, the one method in the program that sets a stroke,
now returns rounded caps and joins for all of its call sites at once, and the
shared primitives in ComponentDrawContext draw a rounded body, a filled accent
handle and a consistent pin. What each painter draws inside its own symbol is
untouched, because an AND gate has to stay an IEEE AND gate.

Along the way, three defects:

Printing and image export took the signal colours from the theme. Exporting a
figure while the dark theme was showing drew it in near-white ink on a white
page. Print now has its own palette, separate from the light theme, and
PrintViewColorsTest holds every colour in it to three-to-one against white and
checks that the states stay apart from one another.

A change announced by the preference store was mirrored as a colour the user
had chosen. Such a notification arrives on another thread and carries no value,
so the monitor answering it fell back to the default it was constructed with --
for the drawing colours, the light one. The dark theme therefore stored the
light theme's ink under its own name and kept it, which is why retuning the
shipped palette appeared to have no effect at all.

A junction dot shrank while a wire was being dragged: the size factor was
applied twice in one of the two places that draw it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 11:51:58 +02:00
NilsBriggenandClaude Opus 5 c0baa0a072 shell: remember a panel width only when the user drags its divider
Laying the window out moves dividers at sizes that have nothing to do
with what the user chose, so recording every change let the remembered
widths drift: the properties panel came back wider on each run until it
hit its limit. The divider itself is watched for a drag instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:32:52 +02:00
NilsBriggenandClaude Opus 5 c85fd860c7 gui: theme the splash and about windows, drop the old split panes
Both were white with a two-pixel black frame drawn by the application
itself, and the about panel placed its logo and credits at fixed
positions computed for one layout or the other. They follow the theme
now and use a real layout.

The home-grown horizontal and vertical split panes have no remaining
users: the window shell uses JSplitPane, which the theme styles and
which supports keyboard resizing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:29:49 +02:00
NilsBriggenandClaude Opus 5 28942ba343 prefs: replace the ten-tab preferences window with a page list
Ten tabs in a row is a poor way to hold ten pages: the strip wraps, the
names get cut, and there is nowhere to search. The pages are a list
down the side with a filter over it, the page's name is its heading,
and the window opens at a size that fits its contents.

The development snapshot tool can now render a dialog as well as the
main window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:26:18 +02:00
NilsBriggenandClaude Opus 5 29f7891aa9 shell: show the welcome screen when no file was named
The welcome screen was tied to the project's startup-screen flag, which
is also cleared by selecting the project's first tool and by loading
the default libraries, both of which happen before the user has touched
anything: the screen was built and immediately replaced by an empty
grid. The start-up code now asks for it directly, which is the thing it
actually knows.

The canvas no longer replaces the welcome screen behind the user's
back: an editor requested while it is up is remembered and applied when
it is dismissed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:21:06 +02:00
NilsBriggenandClaude Opus 5 2e20217caf shell: open on a welcome screen rather than an empty grid
Started without a file, the application opened straight onto an empty
canvas: nothing about what it is, no way back to yesterday's work, and
a blank page for a beginner to stare at. It now offers a new project,
a file to open, and the projects opened recently. The first edit puts
the canvas back.

The startup flag is set after the window is built, so the project now
tells the window when it changes rather than being asked once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:10:41 +02:00
NilsBriggenandClaude Opus 5 ad70ffce08 shell: float the zoom controls over the canvas, report status below it
The zoom controls sat in the left column under the attribute table,
taking a fixed strip of a panel meant for something else and nowhere
near the drawing they act on. They are now a small floating bar in the
corner of the canvas: zoom out, the level, zoom in, fit, and the grid
toggle, which lights up when the grid is on.

The properties panel showed its own centred bold title above a
header-less table and hid the state readout behind a second tab. The
panel heading names what is selected and the state is a section that
folds away.

The clock rate was painted across the user's circuit in 28-point
monospace and the single-step message in blue over the top left. Both
are labels in the status bar now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:07:10 +02:00
NilsBriggenandClaude Opus 5 cf54c355e1 shell: add a Circuits view and quieten the library tree
Moving between circuits meant finding one in a tree that also listed
every component library, hundreds of entries deep. The side panel now
has a Circuits view holding only the project's own circuits and VHDL
entities, with a filter, the current one marked in the accent colour,
and the circuit actions on its context menu. Libraries and the
simulation are separate views, chosen from the activity bar.

The folder beside a library was a drawing of a manila folder with a
sheet of paper and a shadow, in yellow and grey whatever the theme; a
dozen of them were the loudest thing on screen. It is now an outline in
the interface colour.

The canvas no longer draws a focus ring across the middle of the
window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 10:01:23 +02:00
NilsBriggenandClaude Opus 5 5547d18988 shell: rebuild the main window around an activity bar and editor tabs
The window was two tabbed panes used as dock headers, joined by
home-grown split panes, with the zoom widget wedged under the property
table and no status bar at all. Choosing a circuit replaced what was on
screen with no way back except finding it in the tree again, and
transient messages were painted over the user's circuit in 28-point
monospace.

The window is now an activity bar and one side panel, a tabbed editor
area over the single canvas, an inspector of its own on the right, a
drawer for the VHDL console, and a status bar. The tab strip is a view
of EditorTabModel, which holds the rules about what a tab is and what
closing one does, and is tested on its own.

Panel sizes move from fractions of the window to pixels, since a side
panel needs the width its contents need. The old fractions convert
once, capped so a quarter of a wide screen does not become the width of
the explorer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 09:56:47 +02:00
NilsBriggenandClaude Opus 5 acbc8ae59e theme: give the interface two themes of its own
Any installed look and feel could be selected, so nothing the
application painted itself could be designed to match: every panel,
icon and canvas colour had to survive a decade of unrelated themes.
There are now exactly two, a light and a dark theme built on FlatLaf
from one palette, with the window's own title bar and the menu bar
inside it. The default follows the desktop's light or dark setting.

Interface icons come from a vector set (Lucide, ISC) rather than
bitmap GIFs rescaled at high DPI, and take their colour from the theme.

Drawing colours are kept per theme instead of being overwritten on
every theme change, which used to discard any colour the user had
picked. An integer preference now updates its cached value when it is
set, rather than leaving it to a listener on another thread; reading
back a colour just written returned the previous one.

Also fixes the application failing to start from a build directory,
where a class path entry that does not exist was opened as an archive.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 09:33:43 +02:00
NilsBriggenandClaude Fable 5.1 777c788b6b gui: theme and scale the remaining hard-coded visuals
The auto-zoom button was an unscaled white disc with magic offsets,
the grid toggle icon and the explorer's bold font ignored the look and
feel, the hex editor selection was a fixed pale blue, the FPGA settings
drew black 2px borders and its report used a raw monospaced font.
Fonts built from "Sans Serif", which is not a logical font name,
silently fell back; a test now rejects that spelling.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 16:46:20 +02:00
NilsBriggenandClaude Fable 5.1 c60fe36125 gui: keyboard and screen-reader conventions for dialogs and menus
There is no common dialog base class, so Escape closed three dialogs,
Enter activated a default button in one, and no menu had a mnemonic.
Escape now closes the preferences and options windows and the plain
dialogs, the ones with an OK button make it the default, and the menu
bar derives a mnemonic for every menu from its title after each
relabel, so translations need no markers.

Toolbar buttons are icon-only and had an empty accessible name; they
now announce their tooltip. The attribute table shows a value in full
as a tooltip when its column cuts it off.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 16:42:30 +02:00
NilsBriggenandClaude Fable 5.1 ad55dc9a6e prefs: make every menu shortcut rebindable and fill the gaps
The File, Edit and Project menus hard-coded most of their accelerators,
so the hotkey settings could neither show nor change them, and a user
binding could silently collide with one. Each is now a preference whose
default is exactly the literal it replaced, so nothing changes for
anyone who never opens the settings. Preferences, Add Circuit, Analyze
Circuit, Circuit Statistics and Project Options gain shortcuts.

Reset to defaults set Close Window to Ctrl+W although its declared
default is Shift+Ctrl+W; a test now checks that reset restores every
declared default.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 16:37:02 +02:00
NilsBriggenandClaude Fable 5.1 0d607ff74b search: index circuits, preference tabs and recent files
The omni-search dialog only knew menus and placeable components, so the
most common navigation act, showing another circuit, still meant hunting
the explorer tree. Three providers cover circuits and VHDL entities,
the preferences tabs, and the recent-files list.

PreferencesFrame now describes its tabs statically so they can be
listed without building the window, and the recent-files menu's
open-and-close-blank-window logic moves to ProjectActions so both entry
points share it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 16:30:40 +02:00
NilsBriggenandClaude Fable 5.1 97fda645d4 gui: report failures that used to pass silently
Logging to a file switched itself off on an IOException without telling
the user. Failed attribute edits, an unreadable log file and a simulator
crash showed a bare message or a raw JOptionPane with no title. A crash
while creating a window dumped the stack trace into a parentless dialog.

OptionPane now resolves a parent window for every dialog it shows, the
remaining raw JOptionPane call sites go through it, and project load and
save show a busy pointer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 16:25:58 +02:00
NilsBriggenandClaude Opus 5 b228c303f8 gui: add a proper error dialog and a busy pointer
Failures were reported with showMessageDialog(parent, message), which produces
a dialog with no title and no icon, so a bare exception message arrived with no
indication of what had failed. Some call sites passed no parent at all, and such
a dialog can open behind the main window, where the user never sees it while the
application appears frozen.

Extend the existing OptionPane wrapper rather than adding a parallel helper: it
is already used 188 times against 10 raw JOptionPane calls, and it already
handles the headless case by logging.

showError() takes a title and, optionally, the throwable. The stack trace is
hidden behind a Details button, since it belongs in a bug report rather than in
front of someone building a circuit. Messages are escaped before going into the
HTML label so markup in an exception is not interpreted.

WaitCursor shows a busy pointer for short blocking work on the event thread,
restoring it even when the task throws. Anything long enough for the user to
consider giving up still belongs on a SwingWorker with real progress, as the
circuit analyser already does.

The pieces that can be checked without a display, the stack trace rendering,
HTML escaping and parent resolution, are separate static helpers with tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 16:14:19 +02:00
NilsBriggenandClaude Opus 5 9207f7478a prefs: stop double-scaling the interface on high-DPI displays
The default interface scale was the screen height divided by a thousand. On any
display the operating system scales, Java2D already enlarges the whole interface,
so the application enlarged it a second time on top of that.

Defer to the platform where it scales, and fall back to a size-based guess only
on a high resolution display that is not being scaled at all, where the
interface would otherwise be uncomfortably small.

Existing users have the old value stored. Migrate it once, and only when it
still matches what the old formula would produce on this display, so a scale the
user chose themselves is left untouched. The migration runs before SCALE_FACTOR
is created, because the monitor caches the stored value as it is constructed and
static initialisers run in source order.

The restart requirement for a scale change is deliberately left in place; see
the phase notes for why making it live needs wider verification first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:51:07 +02:00
NilsBriggenandClaude Opus 5 3248ae5a56 gui: pad the preferences and project options panels
Both dialogs are built from a shared OptionsPanel base, so their contents sat
flush against the window edge unless a panel happened to add its own border.
Apply the standard panel padding in the two base classes, which gives all ten
preference panels and the project option panels the same inset at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:48:57 +02:00
NilsBriggenandClaude Opus 5 0f7a72180f chrono: make the timing diagram follow the active theme
The chronogram painted itself from hardcoded colours: a white background, a
light grey row band and black waveforms, bus labels and signal names. None of
it was part of applyThemeColors(), so the whole panel stayed light while the
rest of the application went dark, and the waveforms were effectively
unreadable there.

Add DEFAULT_CHRONO_*/DARK_CHRONO_* pairs alongside the existing colour pairs
and build the row palette on each call rather than caching it in static arrays,
so a theme change is picked up without restarting. Waveforms are cached in
offscreen images, so the cache is dropped when the theme changes.

Exported images deliberately keep the light palette, since they are meant to be
dropped into a document rather than viewed in the application.

ThemeColorPairingTest guards the pairs reflectively. It found five colours with
no dark counterpart; all five are intentional and are now documented as such,
notably the text tool colour, which seeds an attribute saved in the circuit
file and so must not follow the theme.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:47:53 +02:00
NilsBriggenandClaude Opus 5 e92ac49525 gui: improve canvas rendering and wheel handling
The painter reset its colour to a hardcoded black after drawing halos and
width-incompatibility markers, which leaves stray black strokes on a dark
canvas. Reset to the themed component colour instead. The diagnostic red,
blue and magenta overlays are deliberately left alone, as those colours carry
meaning rather than following the theme.

Wheel handling used the integer getWheelRotation(), so a trackpad's fractional
gestures were either truncated away or applied as a whole notch, and the zoom
step was a fixed 0.1 however large the gesture. Use the precise rotation, fall
back to the integer one for devices that do not report it, and scale the zoom
by the gesture.

The scroll helper returned 0 when its bounds check failed, which would snap the
scrollbar to the very start rather than stopping at the end of its range.

Relates to #1262.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:43:44 +02:00
NilsBriggenandClaude Opus 5 842439a590 gui: polish the main window chrome
The split pane drag bar painted nothing until it was grabbed, so the panes ran
together with no visible boundary, and its grab area was a fixed six pixels,
which is an awkward target once the interface is scaled up. Draw a hairline
from the theme's separator colour at rest and scale the grab area.

The two docked tab strips built their font by hand at a fixed nine points.
Route both through the shared typographic scale instead, and mark them as card
tabs, a hint FlatLaf themes honour and other look and feels ignore.

The split fraction contract is untouched, so the stored window layout
preferences still load.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:41:45 +02:00
NilsBriggenandClaude Opus 5 97d40d0913 toolbar: give toolbar items real hover, pressed and selected states
ToolbarButton was a bare JComponent whose mouseEntered() did nothing, so
nothing in the toolbar ever reacted to the pointer. The pressed state was a
hardcoded grey rectangle and selection a two pixel outline, neither of which
suits a dark theme.

Make it a JButton so it participates in focus and rollover, and keep the item
responsible only for its glyph through an Icon adapter, since items paint live
circuit components rather than static images. The state backgrounds are still
painted here rather than left to the look and feel, because the user may pick
any installed look and feel and only some of them style a borderless button;
the colours come from the theme with fallbacks so every one of them works.

Separators are items too, so they are marked non-interactive rather than
gaining a hover effect, and they now use the theme's separator colour. They
are deliberately left enabled, as disabling would make Swing substitute a
derived disabled icon and change how they are drawn.

updateUI() re-applies the borderless styling, because switching the look and
feel reinstalls that theme's button border and would otherwise put a frame
back around every item.

The ToolbarModel, ToolbarItem and ToolbarClickableItem interfaces are
unchanged, so per-project toolbar customisation keeps working.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:37:06 +02:00
NilsBriggenandClaude Opus 5 a07ad41478 util: add a shared scale, spacing and typography foundation
Interface fonts and gaps were written in place at each call site, which is
why parts of the application still showed fixed size Serif text that ignored
both the look and feel and the user's interface scale.

Add three small utilities that later user interface work can build on:

  UiScale   a single read path over the scale factor
  Spacing   a 4 pixel spacing scale, scaled, with border helpers
  UiFonts   fonts derived from the look and feel's label font

Then migrate the worst offenders: the log value tables, the combinational
analysis truth table, the assembler window and the FPGA commander windows.
The table fonts become methods rather than constants so that a look and feel
change is picked up without restarting, and the log value column moves to a
monospaced font so values line up.

UiScaleTestSupport documents a trap for later work: PrefMonitorDouble.set()
writes to the preference node but leaves its cached value alone until the
preference listener fires on another thread, so a read straight after a write
still sees the old value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:32:36 +02:00
NilsBriggenandClaude Opus 5 7d2371316d gui: fix tab label scaling and theme the splitter drag indicator
The Design/Simulate tab labels were built with a raw 9 point font while the
attribute tabs below them were passed through AppPreferences.getScaledFont(),
so the two tab strips disagreed and the top one stayed unreadably small on
high-DPI displays.

The split pane drag indicator was a hardcoded translucent black, which is
nearly invisible against a dark look and feel. Derive it from the active
theme instead, falling back to the previous colour when the look and feel
exposes neither an accent nor a separator colour.

VerticalSplitPane.MyDragbar extends HorizontalSplitPane.Dragbar, so both
split panes pick this up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 10:31:36 +02:00