From a4b2aa1b1962da6809eea5f168327078c22222f0 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Tue, 22 Sep 2026 12:33:39 +0200 Subject: [PATCH] Android: the editor's text reaches the assembler, and the tabs come back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two defects found on the emulator, both of which made the app look finished and do nothing. **Assemble ran on an empty program.** `SourceView.codeProperty.register()` at the bottom of `source-view.ts` does `Object.defineProperty` on the prototype, which overwrites the `get code()` / `set code()` written in the class body — those accessors were dead from the moment the module loaded, while looking authoritative. Reads went to NativeScript's property store, whose value is `defaultValue: ""`, and typing changes the inner TextView and never that store. Measured after typing `LDA #$01`: textChange fired "LDA #$01" <- the event is fine textView.text "LDA #$01" <- the inner widget is fine native getText() "LDA #$01" <- Android is fine editorController.code "" <- what Assemble assembled The toast then said only "Assemble failed" — the assembler's own message never reaches the port — while the same program assembles headlessly: `learn6502 assemble` prints "Code assembled successfully, 2 bytes." The typed text goes back into the property now, through `codeProperty.nativeValueChange()`, which is the direction NativeScript provides for "the native side moved" and does not re-enter `valueChanged`'s write-back into the widget the value came from. The dead accessors are gone and a comment says why there are none. **The bottom tab bar measured three pixels.** `Adw.ViewSwitcherBar:reveal` defaults to FALSE: in a GNOME window an `Adw.Breakpoint` asks for the bar once the window goes narrow, and a wide window keeps it collapsed in favour of the header-bar switcher. Android has no wide branch — this shell IS the narrow layout — and nothing ever asked. The app had no visible way to change page, and the shell measured 1517px of a 2400px screen with white below it. One line, and both symptoms go. Verified on the emulator: the four tabs are back at the bottom, the shell fills the screen, and `LDA #$01 / STA $0200 / BRK` now says "Erfolgreich assembliert" with the button switching to Run. Known, not fixed here: after the soft keyboard has been shown once the shell keeps the reduced height (tab bar at y=1455 instead of 2338), and the tab icons render as placeholder glyphs. --- packages/app-android/app/views/main.ts | 8 +++ .../app-android/app/widgets/source-view.ts | 53 ++++++++++++------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/app-android/app/views/main.ts b/packages/app-android/app/views/main.ts index 3301bd5f..aa67cbdd 100644 --- a/packages/app-android/app/views/main.ts +++ b/packages/app-android/app/views/main.ts @@ -294,8 +294,16 @@ export class MainController implements MainView { toolbar.set_content(toast); // Bottom view switcher bar bound to the stack. + // + // REVEALED, explicitly. `Adw.ViewSwitcherBar:reveal` defaults to FALSE — in a GNOME + // window the bar is asked for by an `Adw.Breakpoint` (`setters { bar.reveal: true; }`) + // once the window goes narrow, and a desktop-width window keeps it collapsed in favour + // of the header-bar switcher. Android has no wide branch: this shell is the narrow + // layout, always. Without this line the bar measured THREE PIXELS on the emulator and + // the app had no visible way to change page at all. const switcher = new Adw.ViewSwitcherBar(); switcher.set_stack(stack); + switcher.reveal = true; toolbar.add_bottom_bar(switcher); return toolbar; diff --git a/packages/app-android/app/widgets/source-view.ts b/packages/app-android/app/widgets/source-view.ts index 77393435..07d1b4f7 100644 --- a/packages/app-android/app/widgets/source-view.ts +++ b/packages/app-android/app/widgets/source-view.ts @@ -143,25 +143,17 @@ export class SourceView extends ContentView implements SourceViewWidget { } // Instance methods - public - /** - * Get the source code - */ - get code(): string { - return this.textView ? this.textView.text : this._pendingCode; - } - - /** - * Set the source code - */ - set code(value: string) { - if (this.code === value) return; - - this._pendingCode = value; - if (this.textView && this.textView.text !== value) { - this.textView.text = value; - } - this.notifyPropertyChange("code", value); - } + // + // NOTE — `code` has NO accessor here on purpose. `codeProperty.register(SourceView)` + // (bottom of this file) defines `code` on the prototype itself, so a `get code()` / + // `set code()` written in the class body is overwritten before any instance exists. + // A pair of them lived here and looked authoritative; reads went to NativeScript's + // property store instead, which is why the editor's text never reached the + // assembler. Reading and writing both go through the property now: the widget + // pushes with `nativeValueChange` (see `textChange`), and `valueChanged` writes + // the other way. + /** Defined on the prototype by `codeProperty.register()`; declared so TypeScript sees it. */ + declare code: string; /** * Get whether the source view has code @@ -357,6 +349,29 @@ export class SourceView extends ContentView implements SourceViewWidget { if (this.textView) { const newText = args.value as string; + // THE TYPED TEXT GOES BACK INTO THE `code` PROPERTY, and without this line + // nothing ever reads what the user wrote. + // + // `SourceView.codeProperty.register(SourceView)` at the bottom of this file + // does `Object.defineProperty` on the prototype, which REPLACES the + // `get code()` written above — those accessors are dead from the moment the + // module loads. So `sourceView.code` is NativeScript's stored property value, + // and typing changes the inner TextView, never that store. + // + // Measured on the emulator (2026-09-22), after typing `LDA #$01`: + // textChange fired "LDA #$01" <- the event is fine + // textView.text "LDA #$01" <- the inner widget is fine + // native getText() "LDA #$01" <- Android is fine + // editorController.code "" <- what Assemble actually assembled + // The toast then said only "Assemble failed", because an empty program is a + // failed assembly and the port never showed the assembler's own message. + // The same program assembles headlessly: `learn6502 assemble` -> 2 bytes. + // + // `nativeValueChange` is the direction NativeScript provides for exactly + // this: the native side moved, tell the property, and do NOT re-enter + // `valueChanged`'s write-back into the widget the value just came from. + SourceView.codeProperty.nativeValueChange(this, newText); + this.debouncedHighlighting(newText); this.updateLineNumbers(newText); this.events.dispatch("changed", { code: newText });