diff --git a/packages/app-android/app/app.css b/packages/app-android/app/app.css
index 4bedcba9..bc3127c9 100644
--- a/packages/app-android/app/app.css
+++ b/packages/app-android/app/app.css
@@ -139,20 +139,25 @@ TextView.no-underline {
margin-right: var(--safeAreaInsetRight, 0) !important;
}
-/* Adwaita floating action button (the main Assemble / Run / … button). */
+/* Adwaita floating action button (the main Assemble / Run / … button): a
+ `Gtk.Button` carrying `Adw.ButtonContent`. `height: auto` overrides the fixed 38
+ `.adw-button` sets — the FAB sizes to its icon+label plus this padding, as the old
+ hand-built StackLayout did. */
.adw-fab {
background-color: #3584e4;
border-radius: 9999;
padding: 10 18 10 18;
margin: 16;
+ height: auto;
android-elevation: 0;
}
-.adw-fab-label {
+/* `Adw.ButtonContent`'s label defaults to Adwaita's dark button text and only turns
+ white for `.suggested-action` or dark mode (`.adw-button-content-label` /
+ `.ns-dark .adw-button-content-label` in the theme) — the FAB's accent background
+ needs it white unconditionally, like the old `.adw-fab-label` did. */
+.adw-fab .adw-button-content-label {
color: #ffffff;
- font-weight: 700;
- margin-left: 8;
- vertical-align: middle;
}
.ns-dark .adw-fab {
diff --git a/packages/app-android/app/gtk.ts b/packages/app-android/app/gtk.ts
new file mode 100644
index 00000000..b8087863
--- /dev/null
+++ b/packages/app-android/app/gtk.ts
@@ -0,0 +1,6 @@
+// XML door for `Gtk.*` widgets — `` resolves through
+// this barrel, the same way `~/widgets/index` resolves ``. A plain XML
+// app (no Angular/Vue) has no `registerElement` global to register widgets through,
+// so `@gjsify/adwaita-nativescript`'s own convention is an app-local barrel per
+// namespace that re-exports the package's `/gtk` (and `/adw`) subpath.
+export * from "@gjsify/adwaita-nativescript/gtk";
diff --git a/packages/app-android/app/widgets/adw-main-button.ts b/packages/app-android/app/widgets/adw-main-button.ts
index 8626b6c9..a1c94962 100644
--- a/packages/app-android/app/widgets/adw-main-button.ts
+++ b/packages/app-android/app/widgets/adw-main-button.ts
@@ -1,6 +1,5 @@
-import { Label, StackLayout } from "@nativescript/core";
import { localize as _ } from "@nativescript/localize";
-import { Gtk, attachRowPressFeedback } from "@gjsify/adwaita-nativescript";
+import { Adw, Gtk, GTK_BUTTON_CLICKED } from "@gjsify/adwaita-nativescript";
import {
systemRunSymbolic,
mediaPlaybackStartSymbolic,
@@ -31,39 +30,38 @@ const MODES: Partial> = {
};
/**
- * Adwaita-styled floating action button: a pill holding a white symbolic icon +
- * label on the accent background. It is stateless w.r.t. the simulator — the shell
- * computes the MainButtonState and calls setState(); a tap invokes
- * onAction(currentAction). Replaces the Material `MainButton`/`Fab` widget.
+ * Adwaita-styled floating action button: a `Gtk.Button` holding an `Adw.ButtonContent`
+ * (white symbolic icon + label) on the accent `.adw-fab` pill background. It is
+ * stateless w.r.t. the simulator — the shell computes the MainButtonState and calls
+ * setState(); a tap invokes onAction(currentAction). Replaces the Material
+ * `MainButton`/`Fab` widget.
*/
-export class AdwMainButton extends StackLayout {
+export class AdwMainButton extends Gtk.Button {
/** Invoked on tap with the current mode's action. Wired by the shell. */
public onAction: ((action: MainButtonAction) => void) | null = null;
- private readonly _icon: Gtk.Image;
- private readonly _label: Label;
+ // Not `_content`: `Gtk.Button` already declares a private field of that name
+ // (its own single-child slot), and TypeScript refuses two private declarations
+ // of the same name across a base/subclass pair.
+ private readonly _buttonContent: Adw.ButtonContent;
private _state: MainButtonState = MainButtonState.ASSEMBLE;
constructor() {
super();
- this.orientation = "horizontal";
- this.className = "adw-fab";
+ // `add_css_class`, not a `className` assignment: the button's constructor already
+ // put `adw-button` there, and a raw overwrite would drop it — worse, it would come
+ // back on the next `styleClasses`/`add_css_class` call, which rebuilds `className`
+ // from the tracked list and has no idea `adw-fab` was ever there.
+ this.add_css_class("adw-fab");
- const icon = new Gtk.Image();
- icon.iconColor = "#ffffff"; // pinned white on the accent pill, both schemes
- icon.verticalAlignment = "middle";
- this._icon = icon;
- this.addChild(icon);
+ // Icon+label IS `Adw.ButtonContent`. `Gtk.Button` already wires the press-darken
+ // (`attachRowPressFeedback`, in its own constructor) — the only app-specific parts
+ // left are the MainButtonState machine below and the `.adw-fab` pill styling.
+ const buttonContent = new Adw.ButtonContent({ iconColor: "#ffffff" }); // pinned white on the accent pill, both schemes
+ this._buttonContent = buttonContent;
+ this.child = buttonContent;
- const label = new Label();
- label.className = "adw-fab-label";
- label.verticalAlignment = "middle";
- this._label = label;
- this.addChild(label);
-
- // Adwaita buttons darken on press; NS only auto-applies that to `Button`.
- attachRowPressFeedback(this);
- this.addEventListener("tap", () => {
+ this.addEventListener(GTK_BUTTON_CLICKED, () => {
const mode = MODES[this._state];
if (mode && this.onAction) this.onAction(mode.action);
});
@@ -83,7 +81,7 @@ export class AdwMainButton extends StackLayout {
return;
}
this.visibility = "visible";
- this._icon.iconName = mode.icon;
- this._label.text = _(mode.label);
+ this._buttonContent.iconName = mode.icon;
+ this._buttonContent.label = _(mode.label);
}
}
diff --git a/packages/app-android/app/widgets/game-console/gamepad.ts b/packages/app-android/app/widgets/game-console/gamepad.ts
index 580b9456..62089ba7 100644
--- a/packages/app-android/app/widgets/game-console/gamepad.ts
+++ b/packages/app-android/app/widgets/game-console/gamepad.ts
@@ -1,5 +1,5 @@
-import type { Button } from "@nativescript/core";
import { GridLayout, Builder } from "@nativescript/core";
+import { Gtk } from "@gjsify/adwaita-nativescript";
import type { GamepadKey, GamepadEventMap, GamepadWidget } from "@learn6502/common-ui";
import { getGamepadKeyCode, getGamepadKeyChar } from "@learn6502/common-ui";
import { EventDispatcher } from "@learn6502/core";
@@ -12,12 +12,12 @@ export class Gamepad extends GridLayout implements GamepadWidget {
readonly events = new EventDispatcher();
// Button references
- private buttonUp: Button | null = null;
- private buttonDown: Button | null = null;
- private buttonLeft: Button | null = null;
- private buttonRight: Button | null = null;
- private buttonA: Button | null = null;
- private buttonB: Button | null = null;
+ private buttonUp: Gtk.Button | null = null;
+ private buttonDown: Gtk.Button | null = null;
+ private buttonLeft: Gtk.Button | null = null;
+ private buttonRight: Gtk.Button | null = null;
+ private buttonA: Gtk.Button | null = null;
+ private buttonB: Gtk.Button | null = null;
constructor() {
super();
@@ -30,12 +30,12 @@ export class Gamepad extends GridLayout implements GamepadWidget {
this.addChild(componentView);
- this.buttonUp = componentView.getViewById