Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions android-build/app/src/main/java/com/perry/app/PerryBridge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,15 @@ object PerryBridge {
seekBar.progress = progress
}

// --- NumberPicker (WheelPicker) callback ---

@JvmStatic
fun setNumberPickerCallback(picker: NumberPicker, callbackKey: Long) {
picker.setOnValueChangedListener { _, _, newValue ->
nativeInvokeCallback1(callbackKey, newValue.toDouble())
}
}

// --- Context menu ---

@JvmStatic
Expand Down
4 changes: 4 additions & 0 deletions crates/perry-api-manifest/src/entries/part_4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ pub(crate) const API_MANIFEST_PART_4: &[ApiEntry] = &[
method("perry/ui", "Slider", false, None),
method("perry/ui", "ProgressView", false, None),
method("perry/ui", "Picker", false, None),
method("perry/ui", "WheelPicker", false, None),
method("perry/ui", "wheelPickerAddItem", false, None),
method("perry/ui", "wheelPickerGetSelected", false, None),
method("perry/ui", "wheelPickerSetSelected", false, None),
method("perry/ui", "ImageFile", false, None),
method("perry/ui", "ImageSymbol", false, None),
method("perry/ui", "loadImage", false, None),
Expand Down
1 change: 1 addition & 0 deletions crates/perry-codegen-arkts/src/emit_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ pub(crate) fn emit_widget(
mutations,
),
"Picker" => emit_picker(args, callbacks),
"WheelPicker" => emit_wheel_picker(args, callbacks),
// Issue #475 — Combobox(initial, onChange) maps to ArkUI
// Select. Runtime-added items (`comboboxAddItem`) are
// currently not folded into the static options array;
Expand Down
15 changes: 15 additions & 0 deletions crates/perry-codegen-arkts/src/tests/containers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,21 @@ fn picker_with_options_and_closure() {
assert_eq!(r.callbacks.len(), 1);
}

#[test]
fn wheel_picker_uses_native_looping_text_picker() {
let mut m = empty_module();
m.init
.push(app_with_body(nmc("WheelPicker", vec![closure_stub()])));
let r = emit_index_ets(&mut m).unwrap().unwrap();
assert!(r
.ets_source
.contains("TextPicker({ range: [], value: '' }).canLoop(true)"));
assert!(r
.ets_source
.contains("perryEntry.invokeCallback1(0, index)"));
assert_eq!(r.callbacks.len(), 1);
}

#[test]
fn combobox_emits_arkui_select() {
// Issue #475 — Combobox(initial, onChange) → Select with onSelect.
Expand Down
1 change: 1 addition & 0 deletions crates/perry-codegen-arkts/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ pub(crate) fn is_widget_factory(name: &str) -> bool {
| "Toggle"
| "Slider"
| "Picker"
| "WheelPicker"
| "Combobox"
| "RichTextEditor"
| "Calendar"
Expand Down
21 changes: 21 additions & 0 deletions crates/perry-codegen-arkts/src/widgets/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,27 @@ pub(crate) fn emit_picker(args: &[Expr], callbacks: &mut Vec<Expr>) -> String {
)
}

/// `WheelPicker(onChange)` -> ArkUI's native looping `TextPicker`.
pub(crate) fn emit_wheel_picker(args: &[Expr], callbacks: &mut Vec<Expr>) -> String {
let onchange = match args.first() {
Some(closure @ Expr::Closure { .. }) => {
let idx = callbacks.len();
callbacks.push(closure.clone());
format!(
".onChange((_value: string, index: number) => {{\n \
perryEntry.invokeCallback1({}, index);\n \
{drain}\
}})",
idx,
drain = drain_loop_body()
)
}
_ => String::new(),
};

format!("TextPicker({{ range: [], value: '' }}).canLoop(true){onchange}")
}

/// Issue #475 — `Combobox(initial, onChange)` → ArkUI `Select([...])` with
/// `.value()` / `.selected()` / `.onSelect()`. ArkUI's Select takes a
/// `SelectOption[]` (each option is `{value: string}`); `.value()` sets
Expand Down
4 changes: 4 additions & 0 deletions crates/perry-codegen-js/src/emit/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ impl JsEmitter {
"ProgressView" | "progressview_create" => "perry_ui_progressview_create",
"Image" | "image_create" => "perry_ui_image_create",
"Picker" | "picker_create" => "perry_ui_picker_create",
"WheelPicker" | "wheel_picker_create" => "perry_ui_wheel_picker_create",
"wheelPickerAddItem" => "perry_ui_wheel_picker_add_item",
"wheelPickerSetSelected" => "perry_ui_wheel_picker_set_selected",
"wheelPickerGetSelected" => "perry_ui_wheel_picker_get_selected",
// Table (issue #192)
"Table" | "table_create" => "perry_ui_table_create",
"tableSetColumnHeader" => "perry_ui_table_set_column_header",
Expand Down
38 changes: 38 additions & 0 deletions crates/perry-codegen-js/src/web_runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,20 @@ function perry_ui_picker_create(items_json, selected, callback) {
return wrapWidget(allocHandle(el));
}

// Issue #5873: persistent wheel/list selector. This follows the native
// one-callback ABI and receives options through wheelPickerAddItem.
function perry_ui_wheel_picker_create(callback) {
const el = document.createElement("select");
el.size = 5;
el.className = "perry-wheel-picker";
el.style.overflowY = "auto";
el.style.scrollSnapType = "y mandatory";
if (typeof callback === "function") {
el.addEventListener("change", () => callback(el.selectedIndex));
}
return wrapWidget(allocHandle(el));
}

function perry_ui_form_create() {
const el = document.createElement("form");
el.addEventListener("submit", e => e.preventDefault());
Expand Down Expand Up @@ -1503,6 +1517,26 @@ function perry_ui_picker_get_selected(h) {
return el ? el.selectedIndex : -1;
}

function perry_ui_wheel_picker_add_item(h, title) {
const el = getHandle(h);
if (!el) return;
const opt = document.createElement("option");
opt.value = el.children.length;
opt.textContent = title;
opt.style.scrollSnapAlign = "center";
el.appendChild(opt);
}

function perry_ui_wheel_picker_set_selected(h, index) {
const el = getHandle(h);
if (el) el.selectedIndex = index;
}

function perry_ui_wheel_picker_get_selected(h) {
const el = getHandle(h);
return el ? el.selectedIndex : -1;
}

// --- Image Operations ---
function perry_ui_image_create_symbol(name) {
const el = document.createElement("span");
Expand Down Expand Up @@ -3601,6 +3635,7 @@ window.__perry = {
perry_ui_progressview_create,
perry_ui_image_create,
perry_ui_picker_create,
perry_ui_wheel_picker_create,
perry_ui_form_create,
perry_ui_section_create,
perry_ui_navigationstack_create,
Expand Down Expand Up @@ -3686,6 +3721,9 @@ window.__perry = {
perry_ui_picker_add_item,
perry_ui_picker_set_selected,
perry_ui_picker_get_selected,
perry_ui_wheel_picker_add_item,
perry_ui_wheel_picker_set_selected,
perry_ui_wheel_picker_get_selected,
// Image
perry_ui_image_create_symbol,
perry_ui_image_create_url,
Expand Down
4 changes: 4 additions & 0 deletions crates/perry-codegen-wasm/src/emit/ui_method_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(super) fn map_ui_method(method: &str, class_name: Option<&str>) -> &'static
"ProgressView" | "progressview_create" => "perry_ui_progressview_create",
"Image" | "image_create" => "perry_ui_image_create",
"Picker" | "picker_create" => "perry_ui_picker_create",
"WheelPicker" | "wheel_picker_create" => "perry_ui_wheel_picker_create",
"Form" | "form_create" => "perry_ui_form_create",
"Section" | "section_create" => "perry_ui_section_create",
"NavigationStack" | "navigationstack_create" => "perry_ui_navigationstack_create",
Expand Down Expand Up @@ -178,6 +179,9 @@ pub(super) fn map_ui_method(method: &str, class_name: Option<&str>) -> &'static
"pickerAddItem" => "perry_ui_picker_add_item",
"pickerSetSelected" => "perry_ui_picker_set_selected",
"pickerGetSelected" => "perry_ui_picker_get_selected",
"wheelPickerAddItem" => "perry_ui_wheel_picker_add_item",
"wheelPickerSetSelected" => "perry_ui_wheel_picker_set_selected",
"wheelPickerGetSelected" => "perry_ui_wheel_picker_get_selected",
// Camera (issue #191) — Web has no live-camera FFI yet, so the
// wasm_runtime.js stubs return 0 / -1. The dispatch entries here
// exist so user code calling `CameraView()` from a browser build
Expand Down
21 changes: 21 additions & 0 deletions crates/perry-codegen-wasm/src/wasm_runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -2832,6 +2832,18 @@ function perry_ui_picker_create(items_json, selected, callback) {
if (selected !== undefined) el.selectedIndex = selected;
return uiAlloc(el);
}
function perry_ui_wheel_picker_create(callback) {
const el = document.createElement("select");
el.size = 5;
el.className = "perry-wheel-picker";
el.style.overflowY = "auto";
el.style.scrollSnapType = "y mandatory";
el._perryCallback = callback;
el.addEventListener("change", () => {
if (el._perryCallback !== undefined) callWasmClosure(el._perryCallback, el.selectedIndex);
});
return uiAlloc(el);
}
function perry_ui_form_create() {
const el = document.createElement("fieldset");
el.style.display = "flex"; el.style.flexDirection = "column"; el.style.gap = "8px";
Expand Down Expand Up @@ -3351,6 +3363,13 @@ function perry_ui_picker_add_item(h, title) {
}
function perry_ui_picker_set_selected(h, index) { const el = uiGet(h); if (el) el.selectedIndex = index; }
function perry_ui_picker_get_selected(h) { const el = uiGet(h); return el ? el.selectedIndex : -1; }
function perry_ui_wheel_picker_add_item(h, title) {
const el = uiGet(h); if (!el) return;
const opt = document.createElement("option");
opt.textContent = title; opt.style.scrollSnapAlign = "center"; el.appendChild(opt);
}
function perry_ui_wheel_picker_set_selected(h, index) { const el = uiGet(h); if (el) el.selectedIndex = index; }
function perry_ui_wheel_picker_get_selected(h) { const el = uiGet(h); return el ? el.selectedIndex : -1; }

// ---------- Image ----------
function perry_ui_image_create_symbol(name) { return perry_ui_text_create("⬜ " + name); }
Expand Down Expand Up @@ -4677,6 +4696,7 @@ const __perryUiDispatch = {
perry_ui_text_create, perry_ui_button_create, perry_ui_textfield_create, perry_ui_securefield_create,
perry_ui_toggle_create, perry_ui_toggle_set_state, perry_ui_slider_create, perry_ui_scrollview_create, perry_ui_spacer_create,
perry_ui_divider_create, perry_ui_progressview_create, perry_ui_image_create, perry_ui_picker_create,
perry_ui_wheel_picker_create,
perry_ui_form_create, perry_ui_section_create, perry_ui_navigationstack_create, perry_ui_canvas_create,
perry_ui_bloomview_create, perry_ui_bloomview_get_hwnd,
perry_ui_lazyvstack_create, perry_ui_lazyvstack_update, perry_ui_table_create,
Expand Down Expand Up @@ -4739,6 +4759,7 @@ const __perryUiDispatch = {
perry_ui_navstack_push, perry_ui_navstack_pop,
// Picker
perry_ui_picker_add_item, perry_ui_picker_set_selected, perry_ui_picker_get_selected,
perry_ui_wheel_picker_add_item, perry_ui_wheel_picker_set_selected, perry_ui_wheel_picker_get_selected,
// Image
perry_ui_image_create_symbol, perry_ui_image_create_url,
perry_ui_load_image, perry_ui_image_set_size, perry_ui_image_set_tint,
Expand Down
25 changes: 25 additions & 0 deletions crates/perry-dispatch/src/ui_table/part_b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,31 @@ pub(crate) const PERRY_UI_TABLE_PART_B: &[MethodRow] = &[
args: &[ArgKind::Widget, ArgKind::I64Raw],
ret: ReturnKind::Void,
},
// ---- WheelPicker (issue #5873) ----
MethodRow {
method: "WheelPicker",
runtime: "perry_ui_wheel_picker_create",
args: &[ArgKind::Closure],
ret: ReturnKind::Widget,
},
MethodRow {
method: "wheelPickerAddItem",
runtime: "perry_ui_wheel_picker_add_item",
args: &[ArgKind::Widget, ArgKind::Str],
ret: ReturnKind::Void,
},
MethodRow {
method: "wheelPickerGetSelected",
runtime: "perry_ui_wheel_picker_get_selected",
args: &[ArgKind::Widget],
ret: ReturnKind::I64AsF64,
},
MethodRow {
method: "wheelPickerSetSelected",
runtime: "perry_ui_wheel_picker_set_selected",
args: &[ArgKind::Widget, ArgKind::I64Raw],
ret: ReturnKind::Void,
},
// ---- NavigationStack ----
MethodRow {
method: "NavStack",
Expand Down
1 change: 1 addition & 0 deletions crates/perry-hir/src/lower/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,7 @@ pub(crate) fn perry_ui_handle_widget(name: &str) -> bool {
| "LazyVStack"
| "NavigationStack"
| "Picker"
| "WheelPicker"
| "Table"
| "TabBar"
)
Expand Down
17 changes: 17 additions & 0 deletions crates/perry-ui-android/src/ffi/canvas_picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,23 @@ pub extern "C" fn perry_ui_picker_set_selected(handle: i64, index: i64) {
pub extern "C" fn perry_ui_picker_get_selected(handle: i64) -> i64 {
widgets::picker::get_selected(handle)
}

#[no_mangle]
pub extern "C" fn perry_ui_wheel_picker_create(on_change: f64) -> i64 {
widgets::wheel_picker::create(on_change)
}
#[no_mangle]
pub extern "C" fn perry_ui_wheel_picker_add_item(handle: i64, title_ptr: i64) {
widgets::wheel_picker::add_item(handle, title_ptr as *const u8);
}
#[no_mangle]
pub extern "C" fn perry_ui_wheel_picker_set_selected(handle: i64, index: i64) {
widgets::wheel_picker::set_selected(handle, index);
}
#[no_mangle]
pub extern "C" fn perry_ui_wheel_picker_get_selected(handle: i64) -> i64 {
widgets::wheel_picker::get_selected(handle)
}
#[no_mangle]
pub extern "C" fn perry_ui_canvas_draw_image(
h: i64,
Expand Down
1 change: 1 addition & 0 deletions crates/perry-ui-android/src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub mod toggle;
pub mod tree_view;
pub mod vstack;
pub mod webview;
pub mod wheel_picker;
pub mod zstack;

use jni::objects::{GlobalRef, JObject, JValue};
Expand Down
Loading
Loading