Skip to content

Commit d93cd23

Browse files
authored
Android: Implement Material Design 3 List and ListItem components (#72)
* Implement Material Design 3 List and ListItem components - Introduced List and ListItem components to adhere to Material Design 3 guidelines, supporting one-line, two-line, and three-line layouts. - Enhanced storybook view to include interactive list items with tap handlers for user interaction. - Updated XML layout to incorporate new list structures and improved styling. - Registered new components in the widget index for accessibility across the application. * Enhance Storybook and ListItem components for Material Design 3 - Updated Storybook view to improve documentation and structure for Material Design 3 components. - Refactored tap handler setup for list items, consolidating logic for standard and selectable items. - Added accessibility features to ListItem, including content descriptions and improved ripple effects. - Cleaned up code for better readability and maintainability. * Refactor List component to enhance styling and structure - Changed the base class of the List component from ContentView to StackLayout for improved layout handling. - Updated the default container color to 'surfaceContainerHigh' and adjusted related styling. - Simplified the XML layout for the debug info, replacing GridLayout with StackLayout and integrating List components for better organization. - Enhanced the ListItem integration within the debug info to provide clearer information on registers and status flags. * Refactor Debugger and Storybook XML layouts for improved structure and styling - Updated debugger.xml to reduce padding and enhance the layout of the DebugInfo component. - Replaced StackLayout with List components in storybook.xml for various list types, improving consistency with Material Design 3. - Enhanced the DebugInfo widget to utilize ListItem components for displaying CPU registers and status flags, providing a modernized look and better organization.
1 parent e22477e commit d93cd23

File tree

8 files changed

+1390
-73
lines changed

8 files changed

+1390
-73
lines changed

packages/app-android/app/views/main/debugger.xml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
actionBarHidden="true" class="bg-surface text-on-surface">
66

77
<ScrollView>
8-
<StackLayout class="p-16">
8+
<StackLayout class="p-4">
99
<!-- Debugger Header -->
1010
<Label text="Debugger" class="h2 text-center mb-16" />
1111

12-
<!-- Information Section -->
13-
<Label text="Information" class="h3 mb-8" />
14-
<StackLayout class="bg-surface-container mb-16 p-8">
15-
<w:DebugInfo id="debugInfo" />
16-
</StackLayout>
12+
<!-- Debug Information Section -->
13+
<w:DebugInfo id="debugInfo" class="mb-16" />
1714

1815
<!-- Messages Section -->
1916
<Label text="Messages" class="h3 mb-8" />

packages/app-android/app/views/main/storybook.ts

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,98 @@
1-
import { EventData } from "@nativescript/core";
2-
3-
import { Page } from "@nativescript/core";
1+
import { EventData, Page } from "@nativescript/core";
2+
import { ListItem } from "../../widgets";
43

4+
/**
5+
* Storybook view controller for demonstrating Material Design 3 components
6+
*/
57
class Storybook {
68
private page: Page | null = null;
79

8-
public onNavigatingTo(args: EventData) {
10+
/**
11+
* Called when navigating to the storybook page
12+
* @param args - Navigation event data
13+
*/
14+
public onNavigatingTo(args: EventData): void {
915
const page = args.object as Page;
1016
this.page = page;
1117

1218
console.log("storybook: onNavigatingTo", this.page);
19+
20+
// Set up list item tap handlers
21+
this.setupListItemHandlers();
22+
}
23+
24+
/**
25+
* Sets up tap handlers for all list items in the storybook
26+
*/
27+
private setupListItemHandlers(): void {
28+
if (!this.page) return;
29+
30+
// Define all list item IDs
31+
const standardItemIds = [
32+
"oneLineItem1",
33+
"oneLineItem2",
34+
"oneLineItem3",
35+
"twoLineItem1",
36+
"twoLineItem2",
37+
"twoLineItem3",
38+
"threeLineItem1",
39+
"threeLineItem2",
40+
];
41+
42+
const selectableItemIds = [
43+
"selectableItem1",
44+
"selectableItem2",
45+
"selectableItem3",
46+
];
47+
48+
// Set up handlers for standard items (non-selectable)
49+
this.setupStandardItemHandlers(standardItemIds);
50+
51+
// Set up handlers for selectable items (with toggle behavior)
52+
this.setupSelectableItemHandlers(selectableItemIds);
53+
}
54+
55+
/**
56+
* Sets up tap handlers for standard (non-selectable) list items
57+
* @param itemIds - Array of list item IDs
58+
*/
59+
private setupStandardItemHandlers(itemIds: string[]): void {
60+
if (!this.page) return;
61+
62+
itemIds.forEach((id) => {
63+
const item = this.page.getViewById<ListItem>(id);
64+
if (item) {
65+
item.on(ListItem.tapEvent, () => {
66+
console.log(`List item tapped: ${item.headline}`);
67+
});
68+
} else {
69+
console.warn(`Storybook: List item not found with id: ${id}`);
70+
}
71+
});
72+
}
73+
74+
/**
75+
* Sets up tap handlers for selectable list items with toggle behavior
76+
* @param itemIds - Array of selectable list item IDs
77+
*/
78+
private setupSelectableItemHandlers(itemIds: string[]): void {
79+
if (!this.page) return;
80+
81+
itemIds.forEach((id) => {
82+
const item = this.page.getViewById<ListItem>(id);
83+
if (item) {
84+
item.on(ListItem.tapEvent, () => {
85+
item.selected = !item.selected;
86+
console.log(
87+
`List item tapped: ${item.headline}, selected: ${item.selected}`
88+
);
89+
});
90+
} else {
91+
console.warn(
92+
`Storybook: Selectable list item not found with id: ${id}`
93+
);
94+
}
95+
});
1396
}
1497
}
1598

packages/app-android/app/views/main/storybook.xml

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo"
2-
class="bg-surface text-on-surface">
3-
<ActionBar title="Material Design 3 Colors" class="bg-surface text-on-surface" />
4-
<StackLayout class="p-4">
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
2+
xmlns:w="~/widgets/index"
3+
navigatingTo="onNavigatingTo"
4+
class="bg-inverse-surface text-inverse-on-surface">
5+
<ActionBar title="Material Design 3 Storybook" class="bg-surface text-on-surface" />
6+
<ScrollView>
7+
<StackLayout class="p-4">
58
<!-- Primary Colors -->
69
<Label text="Primary Colors" class="text-xl font-bold mb-4" />
710
<GridLayout columns="*,*" rows="auto,auto" class="mb-8">
@@ -181,5 +184,63 @@
181184
<Label text="Inverse Primary" class="text-sm font-medium" textWrap="true" />
182185
</StackLayout>
183186
</GridLayout>
184-
</StackLayout>
187+
188+
<!-- Material Design 3 Lists -->
189+
<Label text="Lists" class="text-xl font-bold mb-4 mt-8" />
190+
191+
<!-- One-line Lists -->
192+
<Label text="One-line Lists" class="text-lg font-bold mb-2" />
193+
<w:List containerColor="surfaceContainer">
194+
<w:ListItem id="oneLineItem1" headline="One-line item" />
195+
<w:ListItem id="oneLineItem2" headline="With leading icon" leadingIcon="res://code_symbolic" />
196+
<w:ListItem id="oneLineItem3" headline="With trailing text" trailingText="100+" />
197+
</w:List>
198+
199+
<!-- Two-line Lists -->
200+
<Label text="Two-line Lists" class="text-lg font-bold mb-2" />
201+
<w:List containerColor="surfaceContainer">
202+
<w:ListItem id="twoLineItem1" headline="Two-line item" supporting="Supporting text" />
203+
<w:ListItem id="twoLineItem2" headline="With leading icon" supporting="Description goes here"
204+
leadingIcon="res://clipboard_symbolic" />
205+
<w:ListItem id="twoLineItem3" headline="With trailing icon" supporting="Additional details"
206+
trailingIcon="res://play_symbolic" />
207+
</w:List>
208+
209+
<!-- Three-line Lists -->
210+
<Label text="Three-line Lists" class="text-lg font-bold mb-2" />
211+
<w:List containerColor="surfaceContainer">
212+
<w:ListItem id="threeLineItem1" headline="Three-line item"
213+
supporting="This is a longer supporting text that can span multiple lines to provide more context and information." />
214+
<w:ListItem id="threeLineItem2" headline="With icon and text" leadingIcon="res://open_book_symbolic"
215+
supporting="A detailed description that provides comprehensive information about this item in the list."
216+
trailingText="5m" />
217+
</w:List>
218+
219+
<!-- Interactive Lists with Selection -->
220+
<Label text="Interactive Lists" class="text-lg font-bold mb-2" />
221+
<w:List containerColor="surfaceContainer">
222+
<w:ListItem id="selectableItem1" headline="Selectable item" supporting="Tap to select"
223+
leadingIcon="res://build_alt_symbolic" />
224+
<w:ListItem id="selectableItem2" headline="Another selectable" supporting="Tap to toggle"
225+
leadingIcon="res://school_symbolic" />
226+
<w:ListItem id="selectableItem3" headline="Third option" supporting="Try selecting this"
227+
leadingIcon="res://bug_symbolic" />
228+
</w:List>
229+
230+
<!-- List with Dividers -->
231+
<Label text="Lists with Dividers" class="text-lg font-bold mb-2" />
232+
<w:List containerColor="surfaceContainer">
233+
<w:ListItem headline="Item with divider" supporting="First item" showDivider="true" />
234+
<w:ListItem headline="Another item" supporting="Second item" showDivider="true" />
235+
<w:ListItem headline="Last item" supporting="Third item" />
236+
</w:List>
237+
238+
<!-- Disabled List Item -->
239+
<Label text="Disabled State" class="text-lg font-bold mb-2" />
240+
<w:List containerColor="surfaceContainer">
241+
<w:ListItem headline="Enabled item" supporting="This item is interactive" />
242+
<w:ListItem headline="Disabled item" supporting="This item cannot be tapped" enabled="false" />
243+
</w:List>
244+
</StackLayout>
245+
</ScrollView>
185246
</Page>
Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
import { GridLayout, Label } from "@nativescript/core";
1+
import { ContentView, Builder } from "@nativescript/core";
22
import type { DebugInfoWidget } from "@learn6502/common-ui";
33
import type { Simulator } from "@learn6502/6502";
44
import { num2hex, addr2hex } from "@learn6502/6502";
5+
import { ListItem } from "../list-item";
56

6-
export class DebugInfo extends GridLayout implements DebugInfoWidget {
7-
private regALabel: Label | null = null;
8-
private regXLabel: Label | null = null;
9-
private regYLabel: Label | null = null;
10-
private regSPLabel: Label | null = null;
11-
private regPCLabel: Label | null = null;
12-
private flagsLabel: Label | null = null;
7+
/**
8+
* DebugInfo widget displaying 6502 CPU registers and status flags
9+
* Uses Material Design 3 List and ListItem components for a modern look
10+
*/
11+
export class DebugInfo extends ContentView implements DebugInfoWidget {
12+
// ListItem references for registers
13+
private itemA: ListItem;
14+
private itemX: ListItem;
15+
private itemY: ListItem;
16+
private itemSP: ListItem;
17+
private itemPC: ListItem;
18+
private itemFlags: ListItem;
1319

1420
constructor() {
1521
super();
@@ -18,33 +24,72 @@ export class DebugInfo extends GridLayout implements DebugInfoWidget {
1824
public onLoaded(): void {
1925
super.onLoaded();
2026

21-
// Get references to labels from template
22-
this.regALabel = this.getViewById<Label>("regA");
23-
this.regXLabel = this.getViewById<Label>("regX");
24-
this.regYLabel = this.getViewById<Label>("regY");
25-
this.regSPLabel = this.getViewById<Label>("regSP");
26-
this.regPCLabel = this.getViewById<Label>("regPC");
27-
this.flagsLabel = this.getViewById<Label>("flags");
27+
console.log("[DebugInfo] onLoaded - loading template");
28+
29+
// Load the XML layout using Builder
30+
const componentView = Builder.load({
31+
path: "~/widgets/debugger",
32+
name: "debug-info",
33+
});
34+
35+
if (!componentView) {
36+
console.error("Failed to load debug-info.xml template");
37+
return;
38+
}
39+
40+
// Get references to list items from template
41+
this.itemA = componentView.getViewById<ListItem>("itemA");
42+
this.itemX = componentView.getViewById<ListItem>("itemX");
43+
this.itemY = componentView.getViewById<ListItem>("itemY");
44+
this.itemSP = componentView.getViewById<ListItem>("itemSP");
45+
this.itemPC = componentView.getViewById<ListItem>("itemPC");
46+
this.itemFlags = componentView.getViewById<ListItem>("itemFlags");
47+
48+
if (
49+
!this.itemA ||
50+
!this.itemX ||
51+
!this.itemY ||
52+
!this.itemSP ||
53+
!this.itemPC ||
54+
!this.itemFlags
55+
) {
56+
console.error("DebugInfo: Failed to find list items in template");
57+
} else {
58+
console.log("[DebugInfo] All list items loaded successfully");
59+
}
60+
61+
// Add the componentView to the content
62+
this.content = componentView;
2863
}
2964

3065
public update(simulator: Simulator): void {
3166
// Get register values from simulator.info
3267
const { regA, regX, regY, regP, regPC, regSP } = simulator.info;
3368

34-
// Update register values
35-
if (this.regALabel) this.regALabel.text = "$" + num2hex(regA);
36-
if (this.regXLabel) this.regXLabel.text = "$" + num2hex(regX);
37-
if (this.regYLabel) this.regYLabel.text = "$" + num2hex(regY);
38-
if (this.regSPLabel) this.regSPLabel.text = "$" + num2hex(regSP);
39-
if (this.regPCLabel) this.regPCLabel.text = "$" + addr2hex(regPC);
69+
// Update register values with both hex and decimal representation
70+
if (this.itemA) {
71+
this.itemA.trailingText = `$${num2hex(regA)} (${regA})`;
72+
}
73+
if (this.itemX) {
74+
this.itemX.trailingText = `$${num2hex(regX)} (${regX})`;
75+
}
76+
if (this.itemY) {
77+
this.itemY.trailingText = `$${num2hex(regY)} (${regY})`;
78+
}
79+
if (this.itemSP) {
80+
this.itemSP.trailingText = `$${num2hex(regSP)} (${regSP})`;
81+
}
82+
if (this.itemPC) {
83+
this.itemPC.trailingText = `$${addr2hex(regPC)} (${regPC})`;
84+
}
4085

41-
// Update flags (NV-BDIZC format)
42-
if (this.flagsLabel) {
86+
// Update flags (N V - B D I Z C format, bits 7-0)
87+
if (this.itemFlags) {
4388
let flagsText = "";
4489
for (let i = 7; i >= 0; i--) {
4590
flagsText += (regP >> i) & 1;
4691
}
47-
this.flagsLabel.text = flagsText;
92+
this.itemFlags.trailingText = flagsText;
4893
}
4994
}
5095
}

0 commit comments

Comments
 (0)