#### Brief overview of PR changes/additions
- "Copy as image" with nothing selected now copies the visible screen
(timestamps included) instead of doing nothing. With a selection it is
unchanged.
- Copy, Copy HTML and Search on ... genuinely need a selection, so
without one they are greyed out with a tooltip saying so rather than
silently doing nothing.
- Copy as image no longer wipes the clipboard when it runs out of its 3s
budget, crops the drawn lines instead of squashing the whole selection
to fit, and copies a run of blank lines rather than nothing.
#### Motivation for adding to Mudlet
Right-clicking the console and picking "Copy as image" left nothing on
the clipboard, with no hint that a selection was needed.
#### Other info (issues closed, discussion etc)
Fixes#9715
Also fixes two ways a selection could outlive the lines it covers:
clearing a console now clears its selection (`clearWindow()` then "Copy
as image" aborted on Qt's bounds assert), and a selection stranded by
the buffer hitting its size limit is followed down with its lines
instead of copying whatever took their place.
New `CopyAsImageTest` functional test, 15 cases. Verified on X11 with
`xclip -selection clipboard -t image/png -o`.
Assisted-by: Claude:claude-opus-5
**Test case:** Right-click the main console with nothing selected, pick
"Copy as image", and paste - you get a picture of the screen. Select
some text and repeat - you get just the selection.
#### Brief overview of PR changes/additions
- MXP `<FRAME>` windows were positioned against the whole main window,
so an edge-aligned frame landed on top of space a package had reserved
with `setBorderRight()` and friends. They now lay out inside the area
the user borders leave.
- Frames are repositioned when those borders or the window size change,
instead of staying where they were first put.
- Frames in a window of their own (`EXTERNAL`) are left alone by that
repositioning.
#### Motivation for adding to Mudlet
With the base UI installed, a game using MXP frames drew its frames on
top of the UI panel instead of beside it.
#### Other info (issues closed, discussion etc)
Fixes#9698
New functional test `MxpFramePlacementTest` (16 cases) covers each edge,
border changes, window resizes, nested frames, external frames, and a
Geyser `Adjustable.Container` attached to the right border, which is how
the base UI reserves its space. 8 of the cases fail on `development`.
**Test case:** With the base UI installed, connect to
`eden-test.rpgframework.de 4000` - the MXP frames sit beside the UI
panel rather than under it.
Assisted-by: Claude:claude-opus-5
#### Brief overview of PR changes/additions
- `lowerWindow()` moved the main display below the full-window
background widget, so with a background set the whole console (text,
split, scrollbar, command line) vanished for the rest of the session; it
now keeps the background bottom-most.
- `setBorderColor` lived only in `mpMainFrame`'s palette, which
`changeColors()` rebuilds from constants - a game sending an OSC palette
change wiped it with no user action at all - and `getBorderColor()`
returned `0,0,0` under a full-window background. The colour is now
stored on the console.
- `cover` mode scaled the whole source before cropping, so a 3000x100
image in a 1920x1080 window built a 32400x1080 (~140MB) intermediate on
every resize event; it now crops to the target aspect first. Measured
133.5MB/15ms to 0.1MB/3ms, and 890MB for a 20000x100 source.
#### Motivation for adding to Mudlet
All three are in the 5.0 full-window background feature and the first
one makes an ordinary `lowerWindow()` call blank the entire console.
#### Other info (issues closed, discussion etc)
Finding C10 of the 5.0 QA sweep. Introduced by `ae6b017c8` "add:
full-window background image/gradient support" (#9394).
`raiseWindow("main")` and `lowerWindow("main")` both return false today
(`"main"` is never registered in any of the window maps), so there was
no way to undo the first bug from a script - verified, not changed here.
New `WindowBackgroundTest` (17 cases). Five of its assertions were
confirmed to fail before the fix and pass after; the crop-then-scale
order is pinned by comparing the installed brush against a crop-first
render, which a scale-first implementation fails.
Assisted-by: Claude:claude-opus-5
**Test case:** `setBackgroundImage("main",
getMudletHomeDir().."/bg.png", "cover", true)` then `createLabel("l",
10, 10, 100, 100, 1)` then `lowerWindow("l")` - the console stays
visible.
#### Brief overview of PR changes/additions
- Deleting the window that owns a command line no longer leaves a
dangling `TCommandLine*` behind in `mSubCommandLineMap`. A new
`TMainConsole::registerSubCommandLine()` is the single place that map is
written, and it hooks `destroyed()` so the entry goes when the widget
does.
- Seven command line Lua functions located their mandatory string at
`lua_gettop(L)`, which is index `0` when they are called with no
arguments - not a valid Lua stack index, so they silently operated on
whatever an earlier call had left on the stack. The index is now
clamped, and `selectCmdLineText` pushes a real result instead of handing
back a stack leftover.
- `scrollUp`/`scrollDown` report an unknown window instead of raising,
and `prefix()`/`suffix()` only colour the text they add - `suffix()`
also puts it after the last character of the line rather than before it.
#### Motivation for adding to Mudlet
The dangling pointer is the serious one. The command line is a child
widget of the miniconsole, user window or scroll box it lives in, so Qt
frees it along with that parent while the map entry survives.
`TConsole::setFont()` walks the whole map, and that walk is reached from
`Host::setDisplayFont()` - so once any package has created and then
deleted a window carrying a command line, simply changing the display
font, its size or its antialiasing in Settings reads freed memory. No
Lua is involved, and most dereferences land in Qt's text internals, so
crashes from this are likely being filed as unrelated Qt text-layout
bugs.
The rest are Lua API correctness: calls that quietly act on unrelated
data, a return value that is really the C function object, a guard that
never fires, and colour and insert positions that land on the wrong
text.
#### Other info (issues closed, discussion etc)
Closes#9643, closes#9647, closes#9651, closes#9652, closes#9661,
closes#9674
`selectCmdLineText` now returns `true`; its wiki entry needs updating to
match.
**Test case:** with the fix reverted, the new
`SubCommandLineLifetimeTest` fails three assertions and then aborts on
an AddressSanitizer heap-use-after-free in `TCommandLine::console()`
from `TConsole::setFont()`, and 17 of the 20 new and un-pended Lua specs
fail too. With it, busted is 2246/0 twice over and ctest is 72/72
serially.
Assisted-by: Claude:claude-opus-5
#### Brief overview of PR changes/additions
- Seven state getters that are the inverse of setters we already ship:
`getUserWindowTitle`, `getUserWindowStyleSheet`, `getCmdLineStyleSheet`,
`getLabelToolTip`, `getScrollBarVisible`, `getMapWindowTitle` and
`getMapWidgetGeometry`
- Each returns nil plus a message when the window, label or map widget
it names does not exist, reusing the matching setter's wording so the
pair reports the same problems the same way
- 39 specs added to the existing `UI_spec.lua` and `Mapper_spec.lua`
#### Motivation for adding to Mudlet
#9630's audit left 11 Geyser/UI rows untestable purely because the state
those functions set could not be read back; this tranche unblocks them
exactly as #9528's getters unblocked the geometry specs. Scripts get the
same readback symmetry as a side effect.
#### Other info (issues closed, discussion etc)
One deliberate behaviour change: `enableScrollBar`/`disableScrollBar`
now record what they were asked for, so `getScrollBarVisible` answers
for a profile that is not the front tab (whose whole console Mudlet
hides) instead of reporting every background profile's scroll bar as
gone. Wiki pages for the seven functions to follow in Area 51.
**Test case:** busted 1868 passed / 0 failed / 0 errors / 17 pending
(baseline 1829), green twice on the same isolated profile, plus ctest;
31 of the new specs verified by breaking the getters - wrong return
values fail 20, making the not-found branches succeed fails 7, and
dropping the empty-name and nil handling fails 8 more.
Assisted-by: Claude:claude-opus-5
#### Brief overview of PR changes/additions
- Updates `setBackgroundImage(target, imageLocation, [mode],
[fullWindow])` / `resetBackgroundImage(target, [fullWindow])` Lua
functions to support painting a background image or gradient behind the
entire main console window, independent of `main`'s own size/position.
- adds a new `cover` mode for true aspect-preserving scale-to-fill;
gradients are supported via the existing `style` mode using Qt's native
QSS gradient syntax
(`qlineargradient`/`qradialgradient`/`qconicalgradient`).
- Unlike previous `setBackgroundImage("main", ...)`, the background
stays fixed and uncropped when `main` is resized via `setBorderSizes()`
or side panels are added.
#### Motivation for adding to Mudlet
Lets players and package authors theme the whole Mudlet window with a
persistent image or gradient, without hacky miniconsole/image-slicing
workarounds that break whenever borders or panel layout change.
#### Other info (issues closed, discussion etc)
Closes#9392
**Test case:**
`setBorderSizes(80,80,80,80)` then
`setBackgroundImage("main", getMudletHomeDir().."/pathto/asset.png",
"cover", true)`
Image fills the border margin around `main`, uncropped, and stays fixed
through further `setBorderSizes()` changes. Combine with
`setBackgroundColor("main", 0,0,0,140)` for a tinted readable box.
`resetBackgroundImage("main", true)` removes it.
`setBackgroundImage("main","background: qlineargradient(x1:0, y1:0,
x2:0, y2:1, stop:0 #1a1a2e, stop:1 #16213e);", "style", true)` paints a
gradient behind "main"
`resetBackgroundImage("main", true)` removes it.
`setBackgroundImage("main", getMudletHomeDir().."/bgtest-assets/bg.png",
"border")` - Continues to add image as before
Example:
<img width="1482" height="846" alt="Screenshot 2026-07-08 at 2 59 17 PM"
src="https://github.com/user-attachments/assets/e2ec9835-a5c3-4f1a-a246-c45de6f80062"
/>
#### Brief overview of PR changes/additions
Convert raw millisecond integer literals at time-duration call sites to
`std::chrono` literals, and add `#include <chrono>` to each touched
translation unit. Examples:
- `QTimer::singleShot(0, ...)` → `QTimer::singleShot(0ms, ...)`
- `mpTimerReplay->setInterval(1000)` → `setInterval(1s)`
- `mPendingTimer.start(60000)` → `start(1min)`
- `QObject::startTimer(50)` → `startTimer(50ms)`
- `QTest::qWait(100)` → `QTest::qWait(100ms)`
- `QThread::msleep(10)` → `QThread::sleep(10ms)`
This is a semantics-preserving refactor - every duration is kept exactly
equal to before (e.g. `1000` ms becomes `1s`, `60000` ms becomes
`1min`). No behavioural change.
#### Motivation for adding to Mudlet
Chrono literals make time durations self-documenting and type-safe. `1s`
/ `100ms` read unambiguously where a bare `1000` / `100` forces the
reader to remember each API's unit, and the compiler now rejects unit
mismatches. Only genuine duration arguments were converted - loop
counts, scroll-line counts, sizes, ports and the like were deliberately
left as plain integers.
All targeted APIs provide `std::chrono` overloads in the minimum
supported Qt (6.8.2): `QTimer::singleShot`/`start`/`setInterval` (5.8),
`QObject::startTimer` (5.9), `QThread::sleep(std::chrono::nanoseconds)`
(6.6) and `QTest::qWait(std::chrono::milliseconds)` (6.7).
#### Other info (issues closed, discussion etc)
Test case: the full application builds cleanly and the entire functional
`ctest` suite passes. The only failing test is the known, pre-existing
`PasswordMigrationTest` LSan exit-leak (GTK3/fontconfig noise), which is
unrelated to this change.
Assisted-by: Claude:claude-opus-4-8
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove unnecessary else/else-if after return, break, continue, and throw
statements - and in places where fixing them is more trouble than its
worth, added NOLINT.
#### Motivation for adding to Mudlet
https://clang.llvm.org/extra/clang-tidy/checks/readability/else-after-return.html,
so it doesn't pop up in PR reviews.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
- Restored the Central Debug Console's painted black background, lost in
4.20.0 when consoles were made transparent for miniconsole opacity
(#7917); the OS window color (gray/white) was leaking through
- Timestamps now use the console's background instead of hardcoded
rgb(22,22,22), on screen and in copy-as-HTML, so they blend instead of
forming a dark band
#### Motivation for adding to Mudlet
Debug output colors (e.g. capture-group purple) were designed for the
black canvas and had become unreadable on the leaked gray background.
#### Other info (issues closed, discussion etc)
Fixes#9187. Follows the same approach #7942 used to restore the main
console's background.
**Test case:** Open the Central Debug Console (Ctrl+0 in the script
editor) and run any command - the console background should be black,
all line colors readable, and timestamps should not show a distinct dark
band.
https://github.com/user-attachments/assets/ae30b0bb-7977-440d-895a-ac3698c82360
---------
Signed-off-by: Vadim Peretokin <vadim.peretokin@mudlet.org>
Co-authored-by: Vadim Peretokin <vadim.peretokin@mudlet.org>
#### Brief overview of PR changes/additions
- the main display is now moved to its new position before being resized
in TConsole::resizeEvent() and refresh(); resizing first left the
enlarged display clipped by its parent frame, so the visible row count
was computed too small
#### Motivation for adding to Mudlet
Shrinking the top border via setBorderTop() left a permanent blank strip
at the bottom of the main display until new output arrived.
#### Other info (issues closed, discussion etc)
Fixes#7693.
**Test case:** fill the screen with text, `lua setBorderTop(300)` then
`lua setBorderTop(0)` - text reflows to fill the full window height
immediately (previously a ~300px blank strip remained at the bottom).
https://github.com/user-attachments/assets/fdb3ddf3-f9fb-4ab6-93a4-fb509c2a2ea6
---------
Signed-off-by: Vadim Peretokin <vadim.peretokin@mudlet.org>
Co-authored-by: Vadim Peretokin <vadim.peretokin@mudlet.org>
#### Brief overview of PR changes/additions
When the main Mudlet window was resized, text kept wrapping at the
previous width instead of using the new one, leaving unused space on the
right. This PR fixes that so wrapping always matches the actual window
size.
It also handles the multi-profile case: when several profiles share one
Mudlet window, resizing while viewing one profile now also corrects the
wrap width for the other profiles (which are hidden in background tabs),
so their text wraps correctly the moment you switch to them.
Profiles opened in their own detached window are unaffected — they
already manage their own size.
#### Motivation for adding to Mudlet
Text wrapping at a fraction of the available width wastes screen space
and makes the main console harder to read, particularly on wider screens
or layouts. This restores the behaviour present in v4.19.1, including
for users who run multiple profiles in the same Mudlet window.
#### Other info (issues closed, discussion etc)
Regression introduced by #7714.
<img width="1728" height="1031" alt="Screenshot 2026-05-16 at 3 43
55 PM"
src="https://github.com/user-attachments/assets/897fe60c-698a-4732-93fe-6f0fb1f0062e"
/>
---------
Signed-off-by: Michael Conley <sousesider@gmail.com>
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
The echo ignoring newlines after a deleteLine() is a regression from
4.19 and 4.20.
This also strips carriage returns from echos which is done normally by
the telnet stream but NOT for the echo path, so now this is consistent
and doesn't try to render a carriage return + newline as a glyph.
#### Motivation for adding to Mudlet
Proper behavior for using echo/cecho/decho/etc when including newlines
and carriage returns.
#### Other info (issues closed, discussion etc)
https://discord.com/channels/283581582550237184/283582068334526464/1495549390159351870
##Examples to test##
For the newline issue, simply make a trigger that does a deleteLine()
followed by an echo(), such as this:
`-- prompt trigger, or perhaps a substitution type trigger
deleteLine()
echo("\nthis should show up on a new line\n")
`
For the carriage return issue:
`
echo("\r\nthis should not have a music note symbol, AND should be on a
new line\r\n")
`
---------
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
The Qt documentation for `QtConcurrent` points out:
> If you include the `<QtConcurrent>` header, the entire Qt Concurrent
module with the entire Qt Core module will be included, which may
increase compilation times and binary sizes. To use individual functions
from the QtConcurrent namespace, you can include more specific headers.
>
> The table below lists the functions in the QtConcurrent namespace and
their corresponding headers:
|Function|Header|
|--------|------|
|`QtConcurrent::run()`|`<QtConcurrentRun>`|
|`QtConcurrent::task()`| `<QtConcurrentTask>`|
|`QtConcurrent::filter()`,<br>`QtConcurrent::filtered()`,<br>`QtConcurrent::filteredReduced()`|`<QtConcurrentFilter>`|
|`QtConcurrent::map()`,<br>`QtConcurrent::mapped()`,<br>`QtConcurrent::mappedReduced()`|`<QtConcurrentMap>`|
#### Motivation for adding to Mudlet
To speed up the build a little by removing stuff that isn't needed.
#### Other info (issues closed, discussion etc)
In doing this I happened to start cleaning up a couple of header files
`T2DMap.h` and then `mudlet.h`, I then got into converting some
`#include`s into forward declarations in a "include-what-you-use" move.
This then rippled through into a (more than 10!) number of files but
should "improve" things.
Note that the ordering of `#include` in many files seems to be rather
haphazard and is due for some serious overhaul - I suggest that we
should actually declare an "official" style for this project so that
everyone knows what it is.
**During the CI/CB process I discovered that Linux and then MacOS builds
were failing because the file referred to by the `#include
<QtConcurrentTask>` header file was missing, yet was present on my local
PC when I was using the Qt framework from the On-line installer.
Initially I suspected a Debian (and then Devuan - as the packaged
version on my own machine also had this defect AND Ubuntu) package
problem; however it now seems to be an upstream Qt issue as the various
Qt versions & OS combinations suggest that Qt themselves fixed it for Qt
6.10:**
| OS | QtVersion | Missing header |
|--------|-----------------------|----------------|
| Windows| 6.11.0 package | No |
| Devuan | 6.8.2 package | Yes |
| Devuan | 6.10.0 online install | No |
| Ubuntu | 6.9.0 package | Yes |
| Debian | 6.8.2 package | Yes |
| MacOS | 6.9.0 package | Yes |
**To fix this I reverted to an `#include <qtconcurrenttask.h>` for Linux
and MacOS builds - although it would probably have been better to make
it conditional on the Qt Version instead...**
*I have reported this upstream to Debian - see:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1135197*
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
One of a series of PRs, each addressing a type of issue reported by
Clazy.
This is the: "Use multi-arg instead [clazy-qstring-arg]" one.
#### Motivation for adding to Mudlet
Remove warnings detected by the Clazy tool - either when explicitly run
on the Mudlet code-base or detected by the background scanner/analyser
that Qt Creator offers.
#### Other info (issues closed, discussion etc)
A summary I found for this is:
>Using multi-arg methods is recommended for better performance and
reduced
memory usage in `QString` operations. This approach is encouraged to
optimise code efficiency.
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Simplify some of the overly long Mudlet tooltips and texts, and migrate
some text to the wiki.
#### Motivation for adding to Mudlet
Fix https://github.com/Mudlet/Mudlet/issues/1885
#### Other info (issues closed, discussion etc)
Wiki updates are still pending.
---------
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Fix a few things from
4c0e4b764e,
see below for details.
#### Motivation for adding to Mudlet
QA improvements.
#### Other info (issues closed, discussion etc)
1. getAndClearTempDisplayFont() called .value() without checking if the
optional was populated. The only caller is TMainConsole::TMainConsole()
at line 57. If setDisplayFont() in the Host constructor returns early at
line 1129 (because averageCharWidth() == 0 for the font),
mTempDisplayFont is never set. Calling .value() on an empty optional is
UB - in a no-exceptions build (which Mudlet uses), this is a crash with
no error message. The sibling method getDisplayFont() at line 4942
already had the correct guard pattern. We replicated it.
2. setFont(mDisplayFontDetails.makeFont(), true) at line 1606 calls
refreshView() internally (line 1598). Then setFontName() called
refreshView() again at line 1607. Each refreshView() sets fonts on both
TTextEdit panes, recalculates screen dimensions (updateScreenView()),
and forces a full repaint (forceUpdate()). Every font name change was
doing all of this twice.
3. mpHost is a QPointer<Host> that becomes null when the Host is
destroyed. The code dereferenced mpHost->mpConsole without checking
mpHost first. During profile teardown, TConsole can outlive Host
briefly. Other methods in the same class (e.g. raiseFontChangeEvent() at
line 1555) correctly guard with if (!mpHost).
4. Every other field in TFontAttributes had a default member initializer
except mStyleStrategy. Both existing constructors set it, so it's safe
today. But if anyone adds a third constructor (or a default constructor)
without remembering to set this field, it's an uninitialized read - UB.
Now defaults to static_cast<QFont::StyleStrategy>(QFont::NoAntialias |
QFont::PreferQuality), matching the bool constructor's false-case
behavior.
5. The CentralDebugConsole branch in changeColors() previously set font
properties and applied them to both panes. After the font rework it
became empty, but the comment "// No-op now?" with a question mark
signals the author wasn't sure if this was correct. It is correct - font
is now managed via QWidget::font() inheritance and propagated through
updateConsolesFont(). Replaced with a definitive explanation.
6. The setFont() comment said "This *should* be overridding the (void)
QWidget::setFont(const QFont&) method but doesn't seem to be...!" -
implying the intent was to override and something is broken. In reality,
QWidget::setFont is non-virtual so it can never be overridden. The extra
bool forceChange parameter also gives it a different signature. This is
intentional method hiding, not a failed override. The confused comment
could mislead someone into "fixing" it. Replaced with an accurate
explanation of why the hiding is needed.
7. Various typos fixed: overridding, constuctor, QOject, accessng,
releated, inconsistant, Accomodate, TFontDetails, and a stale mKerning
comment.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Fix insertText newline regression in 4.20 that came with
https://github.com/Mudlet/Mudlet/pull/7714.
#### Motivation for adding to Mudlet
Fixes https://github.com/Mudlet/Mudlet/issues/8945
Fixes https://github.com/Mudlet/Mudlet/issues/8824
#### Other info (issues closed, discussion etc)
Added all test cases we found as test cases to our CI to ensure we don't
regress.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Pass large objects by const reference instead of by value
#### Motivation for adding to Mudlet
Fixes code scanning alerts, and reduces memory pressure from Mudlet
(don't have to create and de-allocate so often)
#### Other info (issues closed, discussion etc)
## Summary
Adds a `"title"` property to OSC 8 hyperlink configuration that renders
a non-clickable section header at the top of right-click context menus.
This gives players immediate context about what a menu relates to (e.g.,
an item name, NPC name, or action category).
Context menus now also use the main console display font set in
Settings, matching the rest of the game output.
## Configuration
**Simple string** (renders with default teal color):
```json
{"title": "Lamb and Barley Stew", "menu": [{"View Details": "send:look stew"}, {"Buy": "send:buy stew"}]}
```
**With Tier 2 style properties:**
```json
{
"title": {
"text": "Lamb and Barley Stew",
"style": {"color": "#5fbdaf", "bold": true}
},
"menu": [
{"View Details": "send:look stew"},
{"Buy": "send:buy stew"}
]
}
```
**Compact syntax** (shorthand `ti` for `title`):
```json
{"ti": "Lamb and Barley Stew", "m": [{"View Details": "send:look stew"}, {"Buy": "send:buy stew"}]}
```
## Title style
The title's `style` object uses the same [Tier 2 style
properties](https://wiki.mudlet.org/w/Manual:Supported_Protocols#Style_Properties)
as OSC 8 hyperlink styling, including all supported color formats (named
colors, hex, rgb). Currently supported properties for the title:
| Property | Type | Description |
|----------|------|-------------|
| `color` | String | Title text color (default: `#5fbdaf`) |
| `bg` | String | Title background color |
| `bold` | Boolean | Bold text |
| `italic` | Boolean | Italic text |
## Behaviour notes
- Title renders as a non-clickable label with a separator line below,
appearing above all menu items
- If `title` is set but no `menu` is configured, the title has no effect
(no popup is shown for single-action links)
- Left-click behaviour is unaffected
- When no style is specified, the title defaults to teal (`#5fbdaf`) to
visually distinguish it from clickable menu items
- Title without a `style` object is backward-compatible - older clients
that don't recognize the `title` key will silently ignore it
## Files changed
- **`src/TBuffer.h`** - Added `menuTitle` and `menuTitleStyle` fields to
`HyperlinkStyling`
- **`src/TBuffer.cpp`** - JSON config parsing for `title` (string and
object formats), query param extraction and URL stripping
- **`src/TTextEdit.cpp`** - Renders the title label in the popup menu
with style properties; sets console font on context menus
- **`src/TConsole.h/cpp`** - Registers `ti` shorthand and `title` preset
property
## Screenshots
<img width="500" height="329" alt="Screenshot 2026-02-06 at 14 21 56"
src="https://github.com/user-attachments/assets/6d7d047d-2654-4aec-8241-e1ea1614a6f0"
/>
## Test plan
- [x] Send an OSC 8 link with `"title"` as a simple string - verify teal
header appears above menu items
- [x] Send an OSC 8 link with `"title"` as an object with custom style -
verify color/bold/italic apply
- [x] Send an OSC 8 link with menu but no title - verify no header
appears
- [x] Send an OSC 8 link with title but no menu - verify no popup on
right-click
- [x] Verify left-click still executes the primary action
- [x] Change the console font in Settings - verify context menu font
updates to match
- [x] Verify compact syntax `"ti"` works as shorthand for `"title"`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
#### Brief overview of PR changes/additions
Fixes miniconsole text being cut off on the right side after switching
between profiles.
#### Motivation for adding to Mudlet
When users switch between different game profiles, miniconsoles (small
text windows often used for chat, health bars, etc.) would display
incorrectly - with text appearing cut off. This happened because the
console's internal width wasn't being refreshed properly after a profile
switch.
#### Other info (issues closed, discussion etc)
**Root cause:** When switching profiles, Qt's widget geometry isn't
updated until the event loop processes the show/hide events. The
previous code was refreshing subconsoles too early, before the geometry
was correct.
**Solution:**
- Added `TMainConsole::refreshSubconsoles()` to refresh all subconsole
views
- Deferred the refresh call using `QTimer::singleShot(0, ...)` so Qt can
fully process visibility changes and update geometry first
- Added width caching in Geyser's autowrap code as an extra safeguard
against invalid widths
Fixes#8273
#### Brief overview of PR changes/additions
Ran clang-format on all 134 CPP files in src/ using the project's
.clang-format config
#### Motivation for adding to Mudlet
Ensures consistent code formatting across the codebase.
#### Other info (issues closed, discussion etc)
None
**Test case:** Build the project and verify it compiles successfully.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Reduce Mudlet build times
#### Motivation for adding to Mudlet
Address #6765, and give developers a better experience.
#### Other info (issues closed, discussion etc)
Benchmark Results
=== Testing: development (before) (development) ===
Run 1 of 3...
Time: 226.071397436s
Run 2 of 3...
Time: 216.059263209s
Run 3 of 3...
Time: 221.592760908s
=== Testing: PR #8403 (after) (pr-8403) ===
Run 1 of 3...
Time: 191.112400957s
Run 2 of 3...
Time: 193.975717783s
Run 3 of 3...
Time: 196.569316252s
[benchmark-mudlet-build.sh](https://github.com/user-attachments/files/24466717/benchmark-mudlet-build.sh)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
#### Brief overview of PR changes/additions
Implements MXP `<FRAME>` and `<DEST>` tag support, allowing MUD servers
to create custom windows and redirect output to them. Games can now
define dedicated windows for different content types (chat channels,
combat logs, room descriptions, maps, etc.) and control which window
receives specific output.
**Key additions:**
- `TMxpFrameManager` - manages frame lifecycle and hierarchy
- `TMxpFrameTagHandler` - processes `<FRAME>` tags for window creation
- `TMxpDestTagHandler` - processes `<DEST>` tags for output routing
- Modified `cTelnet::postData()` to route output to destination frames
- Support for floating/docked windows, tabs, and nested frame
hierarchies
- Unit tests covering tag parsing and handler behavior
**Supported features:**
- Frame attributes: name, title, position, size, floating/docking,
scrolling
- Output routing with EOL (clear current line) and EOF (clear buffer)
flags
- Parent-child frame relationships with automatic cleanup
- Maximum 20 frames per profile to prevent resource exhaustion
Documentation: [Area
51](https://wiki.mudlet.org/w/Area_51#MXP_FRAME_and_DEST_Tags.2C_PR_.238577)
#### Motivation for adding to Mudlet
Enables server-controlled multi-window layouts, improving information
organization and readability. Players get dedicated windows for
different game systems without manual configuration. This capability is
standard in other MUD clients (MUSHclient, zMUD) and addresses a
long-standing gap in Mudlet's MXP implementation.
#### Other info (issues closed, discussion etc)
Closes#8573
---
### Architecture Overview
```mermaid
graph TB
Server[MUD Server] -->|MXP Tags| Telnet[cTelnet]
Telnet --> MxpProc[TMxpProcessor]
MxpProc --> TagParser[TMxpTagParser]
TagParser --> FrameHandler[TMxpFrameTagHandler]
TagParser --> DestHandler[TMxpDestTagHandler]
FrameHandler --> Client[TMxpMudlet]
DestHandler --> Client
Client --> FrameMgr[TMxpFrameManager]
FrameMgr -->|Creates| DockWidget[TDockWidget]
FrameMgr -->|Creates| TabWidget[QTabWidget]
FrameMgr -->|Creates| Console[TConsole]
FrameMgr -->|Routes Output| ActiveConsole[Active Destination Console]
DockWidget --> MainWindow[QMainWindow]
TabWidget --> MainWindow
style FrameMgr stroke:#2e7d32,stroke-width:3px
style Client stroke:#1976d2,stroke-width:3px
style Server stroke:#c62828,stroke-width:3px
```
---
<img width="1728" height="1013" alt="Screenshot 2025-12-06 at 12 34
24 PM"
src="https://github.com/user-attachments/assets/ed26c581-6b7c-467f-bf35-951586d114b4"
/>
#### Brief overview of PR changes/additions
This PR consolidates three major OSC 8 hyperlink enhancements that
significantly expand interactive capabilities:
**Hyperlink Visibility (PR #8623):**
- Automatic hiding and revealing of hyperlink text based on time delays
or user input
- Support for conceal, reveal, and reveal-then-conceal actions
- Expire triggers on prompt, input, or output events
- Progressive disclosure interfaces and temporary hints
- Experimental: requires `lua setConfig("experiment.osc8.visibility",
true)` to activate.
**Hyperlink Selection (PR #8650):**
- Interactive, stateful links that can be toggled on and off
- Radio button mode (exclusive selection) and checkbox mode (multiple
selection)
- Visual feedback with selected/disabled pseudo-class styling
- Server callbacks with selection state for game integration
**Compact Syntax (PR #8662):**
- Shorthand property names reducing JSON size by 30-80%
- Style preset system for reusable configurations
- Bandwidth optimization for games generating many links
- Backward compatibility with full JSON syntax
Documentation: [Area
51](https://wiki.mudlet.org/w/Area_51#OSC_8:_Hyperlink_Protocol)
https://github.com/user-attachments/assets/d9240835-fb4a-4fd9-b1c7-387b22d8b7f0
#### Motivation for adding to Mudlet
These enhancements transform OSC 8 hyperlinks from simple clickable text
into a comprehensive interactive UI framework for MUD games:
- **Rich Interactivity**: Enable sophisticated interfaces with temporary
hints, dismissible prompts, and stateful controls
- **Bandwidth Efficiency**: Compact syntax reduces network overhead for
link-heavy games
- **User Experience**: Progressive disclosure and contextual controls
create cleaner, more intuitive interfaces
- **Game Integration**: Selection callbacks and visibility triggers
allow dynamic, responsive UI elements
This aligns with Mudlet's "powerful simplicity" philosophy - providing
advanced capabilities while maintaining clean, uncluttered interfaces.
#### Other info (issues closed, discussion etc)
- Consolidates functionality from PRs #8623, #8650, and #8662
- Builds on existing OSC 8 infrastructure (PRs #7828, #8262)
- Full documentation available in Area 51:
https://wiki.mudlet.org/w/Area_51#OSC_8:_Hyperlink_Protocol.2C_PR_.237828.2C_.238262
- Maintains backward compatibility with existing OSC 8 implementations
- Includes comprehensive capability detection via NEW-ENVIRON variables
- These links can be tested by connecting to a profile and entering `say
!osc8-docs` on the command line
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Address feedback in event handling from no-op operations
#### Motivation for adding to Mudlet
Address feedback in https://github.com/Mudlet/Mudlet/pull/8444
#### Other info (issues closed, discussion etc)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Property use Qt's APIs when we're handling events.
#### Motivation for adding to Mudlet
Fix#774
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove unnecessary else blocks following return statements throughout
the codebase.
#### Motivation for adding to Mudlet
Better code readability
#### Other info (issues closed, discussion etc)
Other anti patterns are visible in this PR thanks to this, but let's
keep this PR focused on one pattern only.
---------
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Removes the deprecated Qt5 Core5Compat module and migrates all text
encoding functionality to use Qt6's native APIs. This modernizes
Mudlet's codebase to be fully Qt6-compliant.
#### Motivation for adding to Mudlet
The Qt5 Core5Compat module is a compatibility layer that Qt deprecated
for removal in future versions. By migrating away from it now, we ensure
Mudlet remains buildable and maintainable as Qt continues to evolve.
This change has no user-facing impact - all existing functionality
including multi-byte character encodings (UTF-8, GBK, BIG5, EUC-KR) and
custom codepages (CP437, CP667, CP737, CP869, MEDIEVIA) continues to
work exactly as before.
#### Other info (issues closed, discussion etc)
Closes#7386
**Testing completed:**
- ✅ All 14 C++ unit tests passing
- ✅ Application builds successfully on macOS
- ✅ Application launches and runs normally
- ✅ Encoding detection and switching verified (UTF-8, ISO-8859-1)
- ✅ Hunspell spell-checking with custom encodings functional
**Technical details:**
- Replaced QTextCodec with
QStringConverter/QStringEncoder/QStringDecoder
- Refactored 5 custom codec classes from inheritance to standalone
converters
- Created TEncodingHelper utility class for unified encoding API
- Updated 15+ source files across core networking, display, and Lua
subsystems
- Removed Core5Compat from all build configurations (CMake & qmake)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Add red cross to replay button like log button has. Toggle tooltip to
show status. Add some missing translator informational strings. Set
emergency button to follow toggling with QIcon as other buttons do.
#### Motivation for adding to Mudlet
Better UI, can see when replay is recording.
[Screencast_20251116_095302.webm](https://github.com/user-attachments/assets/b22d3e7b-32dc-4666-a217-ea480aa84193)
#### Other info (issues closed, discussion etc)
#### Brief overview of PR changes/additions
Fixed inconsistent scroll distance when first scrolling up with mouse
wheel. Previously the first scroll jumped much further than subsequent
scrolls.
#### Motivation for adding to Mudlet
Users reported confusing behavior where the first scroll-up would jump
7-13x further than expected (issue #8388). This was caused by PR #4345
(2020) which fixed a different scroll issue but accidentally introduced
double-scrolling: the user's scroll executed immediately while a second
compensatory scroll for the split-screen pane executed shortly after via
QTimer.
The fix combines both scroll operations into a single deferred action,
preserving the QTimer pattern needed for Qt layout timing while ensuring
consistent scroll behavior.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Fix command line being hidden after resize.
#### Motivation for adding to Mudlet
Users unable to click on command line after a resize event.
#### Other info (issues closed, discussion etc)
closes#8270closes#8390
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove support for leak detection using MSVC - this functionality hasn't
been in use in a decade, and we're switching to to using
[LeakSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer)
instead.
#### Motivation for adding to Mudlet
Cleaner code.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Fixes focus behavior when using `enableCommandLine()` to add command
lines to UserWindows and SubConsoles. Previously, clicking on these
command lines would cause the main Mudlet window to lose focus.
**Changes made:**
- Updated focus proxy setup to properly handle all console types
(MainConsole, UserWindow, SubConsole)
- Fixed mouse click behavior to focus the clicked console's command line
instead of a different window
#### Motivation for adding to Mudlet
Users reported that after enabling command lines on UserWindows,
clicking them would cause unexpected focus loss on the main window,
disrupting their workflow.
#### Other info (issues closed, discussion etc)
Closes#7777
**Testing:**
1. Create a UserWindow with a command line:
`lua openUserWindow("test")`
`lua enableCommandLine("test")`
2. Click on the UserWindow's command line - it should receive focus
without affecting the main window
3. Click on the main window's display area - main window should regain
focus properly
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Increase the minimum width for search bar, buttons and latency fields so
the splitter doesn't hide widgets. The minimum width is now the total of
all widgets minimum size in this area.
#### Motivation for adding to Mudlet
Better UI.
#### Other info (issues closed, discussion etc)
closes#8269
#### Brief overview of PR changes/additions
Fixes an issue where the button bar (containing timestamp, replay, and
logging buttons) would receive focus instead of the command line when
starting a profile. Now focus properly goes to the command line as
expected.
#### Motivation for adding to Mudlet
When users start a profile, they expect to be able to immediately type
commands. The button bar incorrectly getting focus meant users had to
click on the command line first, which was an unnecessary extra step
that interrupted the workflow.
#### Other info (issues closed, discussion etc)
Closes#8265
The fix prevents button bar container widgets from accepting focus by
setting their focus policy to `Qt::NoFocus`, ensuring focus flows
correctly to the command line during profile startup.
#### Brief overview of PR changes/additions
Fixes miniconsoles unexpectedly showing search bars and control buttons.
This interim solution restricts search bar, timestamp, replay, logging,
and emergency stop buttons to only appear on the main console where they
belong.
#### Motivation for adding to Mudlet
Miniconsoles should have a clean, minimal appearance without control
buttons. The recent appearance of search bars and other controls on
miniconsoles was a visual regression that cluttered the interface and
confused users.
#### Other info (issues closed, discussion etc)
- Closes#8275 - MiniConsoles have a search bar suddenly
- This is an interim fix while a larger architectural refactor is being
developed in a separate PR
- All console control buttons are now properly restricted to MainConsole
only
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Add a splitter between command line and search bar to allow for
expanding the search area text which gets squished on smaller screens.
Saves to global profile for restoration.
#### Motivation for adding to Mudlet
Better looking interface on smaller screens. Allows user custom sizing
of affected screen elements.
#### Other info (issues closed, discussion etc)
https://github.com/user-attachments/assets/5daf5c99-c005-43d4-9974-c8cb407b08a9
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Anti-alias text for errors view and debug view when built with recent Qt
versions
#### Motivation for adding to Mudlet
Bugfix
#### Other info (issues closed, discussion etc)
Before (no AA):
<img width="796" height="229" alt="image"
src="https://github.com/user-attachments/assets/055b6301-f242-409b-bff5-65916c7fe475"
/>
After (with AA):
<img width="796" height="229" alt="image"
src="https://github.com/user-attachments/assets/2f3a6b59-a569-490d-bcb9-b45222ff7d1f"
/>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Fix Qt 6.10 compile warnings
#### Motivation for adding to Mudlet
Cleaner compiles
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
#### Brief overview of PR changes/additions
Use a ` +$` search-and-replace to remove trailing spaces in `.cpp` and
`.h` source code files to remove unwanted trailing whitespace that has
crept into a number of files.
#### Motivation for adding to Mudlet
To clean up files as `git` notices such white-space and objects in some
circumstances - and such spaces are redundant in the source code.
#### Other info (issues closed, discussion etc)
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
If (mType == MainConsole): initialize TConsole settings as they were
before PR #7917
- Opaque background
- Autofilled
- default color: black
Else: use a transparent background
#### Motivation for adding to Mudlet
PR #7917 was applying a transparent background to the MainConsole which
made the borders around the main buffer transparent, altering/breaking
the GUI appearance.
#### Other info (issues closed, discussion etc)
Fixes: #7941
Also fixes `setBorderColor()` for the main console.
#### Brief overview of PR changes/additions
• **Fixed race conditions** in TConsole::getTextAttributes() by taking
coordinate snapshots to prevent Time-of-Check-Time-of-Use (TOCTOU)
issues during buffer access
• **Added cursor fallback** when no selection is active, ensuring
getTextFormat() works consistently with getFgColor() and getBgColor()
• **Fixed boundary checking** that was incorrectly using "size() - 1"
causing failures when selecting the last character in a line (core issue
in bug #5744)
• **Enhanced error handling** in getTextFormat() Lua function with
proper console validation and clearer error messages
• **Added comprehensive test suite** with 1000+ lines of tests covering
edge cases, race conditions, and exact bug reproduction scenarios
• **Added documentation** in TBuffer clarifying proper formatting usage
patterns
#### Motivation for adding to Mudlet
The original issue showed that getTextFormat() would fail when selecting
the last character of a line, while getFgColor() and getBgColor() worked
correctly in the same situation. This inconsistency was caused by an
off-by-one error in boundary checking (`x >= size() - 1` instead of `x
>= size()`) and lack of cursor fallback when no selection was active.
Additionally, race conditions could occur when buffer state changed
between coordinate validation and character access. This PR ensures all
text formatting functions behave consistently and reliably.
#### Other info (issues closed, discussion etc)
• **Closes** [getTextFormat erroneously errors on last character in the
line if selected by itself.
#5744](https://github.com/Mudlet/Mudlet/issues/5744)
• **Implements fix suggested by [@jarlyyn](https://github.com/jarlyyn)**
who identified the root cause in the boundary checking logic
• **Addresses [@SlySven](https://github.com/SlySven)'s concerns** about
handling empty lines and buffer consistency
• **Adds comprehensive test coverage** requested by
[@vadi2](https://github.com/vadi2) including exact reproduction of the
original bug scenario
• **May unblock** [copy2decho ignores italic/bold/underline formatting
#5589](https://github.com/Mudlet/Mudlet/issues/5589) per
[@demonnic](https://github.com/demonnic)
• **Includes extensive diagnostic testing** with 41 test scenarios that
provide debugging output without breaking the build
The fix uses coordinate snapshots to prevent race conditions, adds
proper cursor fallback behavior, and corrects the boundary logic that
was excluding valid last-character selections.
#### Brief overview of PR changes/additions
Removed the added linebreak append from command echoes IF the trigger
engine is processing.
#### Motivation for adding to Mudlet
While this behavior was apparently necessary to keep command echoes from
bunching in the past, with the new word-wrapping and append methods it
was leading to an extra linebreak being inserted between command echoes:

Now fixed:

#### Other info (issues closed, discussion etc)
Steps to recreate issue:
Make a trigger which sends 2+ commands. Make sure the "Show the text you
sent" preference in "Input" is enabled.
Thanks to @termie for noticing this and helping me chase it down!
#### Brief overview of PR changes/additions
* Revert redesign of main font selection in preferences.
* Restructure usage of fonts to remove the somewhat redundent `(QFont)
mDisplayFont` for widgets that inherently have their own (QFont) which
they use for painting operations.
* Provide a means to track only the details of `QFont`s that we care
about which should be more lightweight than holding a complete copy of a
font (in `TFontDetails`).
* Include the "antialisaing" detall for the main display font (only for
the main console) in the active updates that changing the main font in
the preferences produces.
* Apply changes to the font in a `TConsole` to **all** the
`TCommandLine`s associated with it.
#### Brief overview of PR changes/additions
* The previously revised font settings in the preferences that used a
native font selection was not as useful as it seemed - at least the
Windows one offered controls that we did not need or use which meant
they had not actual effects which would be confusing to the end user.
* Storing a separate copy of `QFont`s did not seem productive especially
as it was easy to modify the intrinsic one and not the copy or
vice-versa.
* Adding a separate class to track the font details we ARE interested in
makes it easier to compare fonts and to tell whether two instances are
the same as far as Mudlet is concerned
* It was not clear that the "anti-aliasing" setting was being correctly
applied/used where it was intended - with this PR it is also updated as
it is changed in the prefernces along with the other two settings: font
"family" and "size".
* The font in command-lines now clearly follow the main console or a
sub- console that they belong to.
#### Other info (issues closed, discussion etc)
Once this is done it is perhaps a bit clearer that the Lua API
`setFont(["windowName", ] "fontName")` sets the font family to use for
the main or (if given a name) a sub-console/user-window - and if it is
the "Main console" that also carries through into:
* the Lua script window in the editor
* the error window in the editor
* the "notepad"
* the map info display in the 2D mapper
the font is also replicated in the corresponding "command-lines" for
that "console".
The Lua API `setFontSize(["windowName", ]integer)` sets the font size
for all the places where the correspond font had been set by the
`setFont(...)` call. Setting the font name and size via the Lua API are
two separate operations and they do act seemingly independently of each
other.
Setting these things via the preferences take effect on the main console
(and related things) immediately (except for the mapper - that only
updates when the preferences dialogue is closed) - the anti-alias
setting also acts on these things in the same manner (which it didn't
before IIRC).
As a side effect of the reversion this will close#7907!
I have a separate commit that gives the option for
sub-console/user-windows to track the main console font family (so that
changing the font in there carries through into those other windows) -
optionally they can *also* track the size of the main font (by a
user/scriptable factor) so that as the main font is made smaller or
larger so do the other consoles - however this can be switched off if
not wanted. I've let that as a separate commit because it would just
enlarge this already meaty PR and it can be safely left to be
reviewed/done separately!
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
Make everything below the top-layer of TConsoles transparent, which
enables transparent miniconsole backgrounds by setting alpha values via
<miniconsole>:setcolor(R,G,B,alpha).
Make TTextEdit only draw text backgrounds if the background color != the
background color of the miniconsole.
#### Motivation for adding to Mudlet
It could be nice for some UI effects to have miniconsoles with custom
opacity.
#### Other info (issues closed, discussion etc)
From my understanding, QT transparency can vary between OSes, if anyone
would be willing to test this PR on MacOS I'd appreciate it! It's
working as is on Windows 11 (edit: and Linux (Fedora/GNOME)). I'd also
be happy for anyone willing to double check that this doesn't mess with
their UIs unintentionally! So far my testing suggest that it shouldn't
be an issue.
Fixes#6472
E.g.

Test script:
```
miniwindow = Geyser.MiniConsole:new({name = "indent-test",autoWrap = true})
clearWindow("indent-test")
miniwindow:move(50,10)
miniwindow:setColor(0,0,255,50)
setWindowWrap("indent-test", 30)
cecho("indent-test",
"\n\t\tText with no colors\
\t\t<red>Fg color red\
\t\t<:orange>Bg color orange\
<reset>\t\taaand we're reset!")
clearWindow("main")
cecho("\n\n\nText behind!!! Sadly so hard to read behind other text :(")
cecho("\nText behind!!!")
cecho("\nText behind!!!")
cecho("\nText behind!!!")
cecho("\nText behind!!!")
```
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
- Created `TTextProperties.h` so TBuffer and TTextEdit both share the
same functions for checking the width of graphemes.
- Added the functions necessary to set hanging indent for
(mini-)consoles (exposes Lua function:
`setWindowWrapHangingIndent(str:window, int:hangingIndent)`)
- Deduplicated the process of appending and wrapping text in TBuffer to
follow, generally:
- `append` calls `appendLine` and `wrapLine` and handles buffer
shrinking.
- `appendLine` adds text to buffer.
- `wrapLine` calls `getWrapInfo` then applies wrap, also logs the
wrapped lines.
- `getWrapInfo` checks the width of each grapheme for determining wrap
points instead of the previous method of treating each QChar as width
one.
<details>
<summary>More specifically</summary>
Created TTextProperties.h for one shared source of grapheme width for
both TTextEdit and TBuffer:
- `graphemeInfo::getBaseCharacter` copied over from
`TTextEdit::getGraphemeBaseCharacter`
- `graphemeInfo::getWidth(unicode, mWideAmbigousWidthGlyphs)` copied
from `TTextEdit::getGraphemeWidth(unicode)` (with some modification)
Added `TLuaInterpreter::setWindowWrapHangingIndent` /
`TConsole::setHangingIndentCount` / `TBuffer::setWrapHangingIndent` to
allow setting hanging indents for any (mini)console
Added `WrapInfo(bool isNewline, bool needsIndent, int firstChar, int
lastChar)` class to TBuffer.h for storing information on whether to
indent / where to snip a line to properly wrap it.
Added the inline method `TBuffer::getWrapInfo(const QString& lineText,
bool isNewline, const int maxWidth, const int indent, const int
hangingIndent)` which takes information on the current line of text and
returns a `QList<WrapInfo>` indicating the start / end indices and
indentation to wrap the text (empty -> no wrap).
I think the code speaks for itself, but hand-wavily the algorithm for
wrapping looks like this (glossing over how it handles edge cases and
indentation):
- Use QTextBoundaryFinder to identify each grapheme
- Use QTextBoundaryFinder to identify linebreaks
- loop through the line and track the width of each grapheme -> current
x position
- if the x position plus the expected indentation exceed the maxwidth:
- check if the current character is a space or valid linebreak
- if not, use the boundaryFinder to search backwards from the current
position for a valid linebreak
- if none found, choose current position for linebreak
- record wrap point indices and indentation by adding a new WrapInfo to
the list
- return list of WrapInfos
Removed `TBuffer::wrap` (this is replaced by wrapLine)
Changed `TBuffer::wrapLine(int startLine, int screenWidth, int
indentSize, TChar& format)` to `TBuffer::wrapLine(int startLine, int
maxWidth, int indentSize, int hangingIndentSize)`
`TBuffer::wrapLine` still loops through each line in the buffer from the
startline to the lastline, now calling getWrapInfo for each line and
applying the wrap/indentation.
- screenWidth -> maxWidth for a less misleading var name
- removed TChar& format as this serves no purpose now
- added hangingIndentSize for... hanging indents
- **Note:** wrapline only records a timestamp for the first line of a
multiline-wrap lines and also uses the resulting blank timestamps to
implicitly indicate that the line was previously wrapped (and has its
hanging indent already), i.e.: `const bool isNewline = (time !=
mudlet::smBlankTimeStamp);`.
- finally, the startline to the penultimate line are logged
(consequently, if no wrap action occurs, no logging occurs), as all
previously wrapped lines will no longer be touched by append actions.
`TBuffer::appendLine`: simplified this method to just handle appending
text to the buffer (by removing handling of shrink buffer / console
overflow).
Added `appendEmptyLine` for adding a blank line to the buffer and reduce
duplicated code.
</details>
#### Motivation for adding to Mudlet
Adds functioning hanging indents to all consoles.
Fixes wrapping for 2-width characters (emojis, asian characters, etc.).
Moving all wrapping logic into one method should be much easier to
maintain/optimize, as well as hopefully cutting down on the bugs arising
from having three different implementations being used in parallel.
#### Other info (issues closed, discussion etc)
Fixes#3674Fixes#5564Fixes#5936Fixes#6390Fixes#6432Fixes#6970Fixes#7846Fixes#7892
/claim #5936
/claim #5564
Before benchmark (Mudlet 4.19.1):

After benchmark (commit e6c516e3):

Miniconsole + main console showing first line indentation & hanging
indentation for single and double width chars:

<details>
<summary>Script for generating a log to compare old wrapping vs.
new</summary>
```
function testWrap()
local echoText = {
"12x4567x90",
"12x 567x 0",
"字❤️包裝🙏😇",
"字 ❤️ 包 裝 🙏😇",
"字a❤️🙏b😇",
"字 ❤️ test 🙏😇"
}
setWindowWrap(200)
local success, message, filename, code = startLogging(true)
if code == 1 or code == -1 then print(f"Started logging to {filename}") end
if setWindowWrapHangingIndent then
print("Updated wrapping")
setWindowWrapHangingIndent("main",0)
else
print("Legacy wrapping")
end
for _,indent in ipairs({0,2}) do
local wrapwidth = 5
setWindowWrapIndent("main",indent)
setWindowWrap(200)
cecho(f "Wrap width: {wrapwidth}, indent: {indent}\n")
for _,str in ipairs(echoText) do
for _,action in ipairs({"feedTriggers","echo","cecho","decho","hecho","print","display"}) do
setWindowWrap(200)
cecho(f "\n<green>{action}<reset>:\n")
setWindowWrap(wrapwidth)
_G[action](f "\n{str}\n")
end
setWindowWrap(200)
cecho(f "\n\n<green>Echoed commands<reset>:\n")
setWindowWrap(wrapwidth)
send(str, true)
print("")
send(str..str, true)
end
end
setWindowWrap(200)
cecho("<green>End of log!\n")
startLogging(false)
end
```
</details>
---------
Co-authored-by: Harrison Martin <h_mart05@uni-muenster.de>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
Renames `(bool) TConsole::setFont(const QString&)` to `setFontName`
because it does actually do the latter (stores the NAME of the font to
use) and could otherwise be mistaken for the base class `(void)
QWidget::setFont(const QFont&)` method.
#### Motivation for adding to Mudlet
To reduce the chance of misinterpreting what the method does in
comparison with what the base class function performs.
#### Other info (issues closed, discussion etc)
It is not impossible that we might restructure things so that we send a
reference to an actual font instance rather than the name of a font in
this class. Such a `setFont` method then would then have the `override`
"label" applied to it so as to flag that it is replacing the base class
function, which the method renamed in this PR does **not**.
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This PR updates the main input line to become a secure/password field
when input echoes are disabled. This ensures that accessibility tools do
not read out sensitive information, such as passwords, as users type.
#### Motivation for adding to Mudlet
To improve accessibility and privacy for visually impaired users by
preventing screen readers from announcing passwords or other sensitive
input when echoes are off, addressing issue #6089.
#### Other info (issues closed, discussion etc)
Closes#6089 and #2248.
Implements a11y best practices for secure input fields.
---
Password is masked when echo is off and also not recorded in the history
of the command line.
https://github.com/user-attachments/assets/4e8cc0be-bd84-483e-93e0-bac0f4367a8a
#### Brief overview of PR changes/additions
Adds functions to enable/disable and find out the state of timestamps on
all user accessible windows. Also adds a `sysConsoleSizeChanged` event
that reports changes in the number of columns, rows and ~~rows~~ (Edit:)
*columns* occupied by the time-stamp (which is now localiseable for the
end-users UI) for any user window as well as the main Window.
#### Motivation for adding to Mudlet
Allow UI designers to accommodate the space used for timestamps. Also
allow time-stamps to be enabled on other console besides the main one if
the end- user desired. This is done with three new Lua API functions:
* `disableTimeStamps([windowName])` - turns off the timestamps if they
are on for the main (if no windowName is provided or it is `"main"`, or
the specified subconsole or user windows if it exists) - or returns a
`nil` + error message if they are already turned off.
* `enableTimeStamps([windowName])` - turns on the timestamps if they are
off for the main (if no windowName is provided or it is `"main"`, or the
specified subconsole or user windows if it exists) - or returns a `nil`
+ error message if they are already turned on.
* `timeStampsEnabled([windowName])` - returns `true` or `false` to
indicate for the main (if no windowName is provided or it is `"main"`,
or the specified subconsole or user windows if it exists) whether
timestamps are shown for the particular console.
#### Other info (issues closed, discussion etc)
These additions were prompted by the Discord user **missionz3r0** in our
mudlet-development channel on 2025/05/20 04:30 UTC
https://www.discord.com/channels/283581582550237184/283582439002210305/1374242960299786352
> Gonna add this to things I'd like to fix/improve when I'm no longer
> moving houses/dead tired.
>
> **Context,**
> As far as I am aware, there is no way to know if the main console is
showing timestamps, nor is there a way to listen in on if they are
toggled. If a package dev tries to borders such that the main console is
exactly the size of the wrap_at * font-size, timestamps will cause the
window to overflow.
>
> **What I'd like to do,**
>* Add a lua function that'll return boolean representing the state of
the timestamp toggle,
>* Raise a `sysSettingChanged` event when timestamps are toggled,
>* Include timestamp characters when calculating `getMainConsoleWidth`
In implementing these changes I realised that there was some duplication
of code for each `TTextEdit` instance in a `TConsole` which was better
done in the parent `TConsole`; it also meant the "slot" that processed
the time-stamp button was moved from the `TTextEdit`s to the `TConsole`
so that it could also update the relevant toolbutton in the bottom
button collection for the `TMainConsole` instance.
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
#### Brief overview of PR changes/additions
Only create (and destroy if not wanted) shortcuts used to provide F3 and
Shift+F3 backwards and forwards search in the main console for those
with accessibility requirements.
#### Motivation for adding to Mudlet
I was using the F3 key for a keybinding and after #7579 I found that it
stopped working - even when I disabled the functionality that it
offered. It turns out that the enabling/disabling steps that the creator
used were not sufficient as it left the "shortcuts" in place even though
they didn't do anything and that was enough to bypass the normal
handling of that key.
#### Other info (issues closed, discussion etc
There was a superfluous initialisation of the `(bool)
Host::mF3SearchEnabled` flag in the Host initialisation list which was
also out of order and producing a compilation warning.
Also cleans up some code miss-formatting (omission of braces around `if`
code blocks and no new line after the condition part).
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>