Skip to content

Component Method Quick Reference ​

For the usage overview (two styles), see README.md; all types are referenced via @yue.

General conventions:

  • Unless stated otherwise, every make accepts the optional parameters style : Array[(String, &StyVal)] — one array mixing numeric (Double) and string values (style key-value pairs applied at creation); these are not repeated in the tables below.
  • on_* methods register callbacks.

Window ​

moonbit
let win = @yue.Window::make(
                title="主窗口",
                size=Some((960.0, 640.0)),
                center=true)
win.on_close(fn(_w) { @yue.quit() })
win.set_content(content_view)

Window::make parameters (no style; windows have no parent layout):

ParameterTypeDefaultDescription
titleString""Title
size(Double, Double)?NoneContent area size (width, height)
on_close(Window) -> Unitno-opClose callback
centerBoolfalseCenter after creation

Common methods:

MethodPurpose
set_title(t)Set title
set_content(v)Set content view
set_content_size(w, h) / get_content_size()Set / read content area size
center() / activate()Center / activate to front
maximize() / unmaximize() / is_maximized()Maximize
set_fullscreen(b) / is_fullscreen()Fullscreen
set_always_on_top(b)Keep on top
set_resizable(b) / is_resizable()Resizable
set_maximizable(b) / set_minimizable(b)Title bar button toggles
set_has_shadow(b) / has_shadow()Window shadow
set_menubar(mb)Attach menu bar
on_close(fn(_w))Close callback
set_should_close(fn() -> Bool)Return false to intercept close

Window::new_with_options parameters (frameless / transparent / overlay windows):

ParameterTypeDefaultDescription
frameBooltruefalse for frameless
transparentBoolfalseTransparent background
no_activateBoolfalseDo not steal focus (overlay / panel-style windows)

Container ​

moonbit
let col = @yue.Container::make(style=[("padding", 12.0)])
col.add_child(child)

Parameters are only style (see the top of this document). Defaults are flexDirection=column, alignItems=stretch; for horizontal layout pass ("flexDirection", "row") in style; see docs/layout.md for key parsing rules.

MethodPurpose
add_child(v)Add child view (any ViewLike)
on_draw(fn(painter))Custom drawing

Label ​

moonbit
let l = @yue.Label::make("文本", style=[("color", "#356AA0")])
l.set_text("新文本")
ParameterTypeDefaultDescription
textStringrequiredInitial text
MethodPurpose
set_text(t)Change text

Button (including Checkbox / Radio) ​

moonbit
let b = @yue.Button::make("确定", on_click=fn() { ... })
let c = @yue.Button::make("启用", button_type=Checkbox, checked=false,
                          on_click=fn() { ... })   // read state via is_checked() in the callback
let r = @yue.Button::make("主题甲", button_type=Radio, checked=true, on_click=...)
b.set_title("新标题")
ParameterTypeDefaultDescription
titleStringrequiredButton text
button_typeButtonTypeNormalNormal / Checkbox / Radio
on_click() -> Unitno-opClick callback
checkedBoolfalseInitial checked state (Checkbox / Radio)

Radio buttons under the same parent container are mutually exclusive automatically; for the initial callback and double notification, see "Inherent Pitfalls" at the end of this document.

MethodPurpose
set_title(t)Change text
is_checked() / set_checked(b)Read / set checked state
on_click(fn())Click callback

Entry ​

moonbit
let e = @yue.Entry::make(text="预填", entry_type=Password)
e.on_activate(fn() { check(e.get_text()) })
ParameterTypeDefaultDescription
textString""Initial text
entry_typeEntryTypeNormalNormal / Password
width_charsInt-1Initial width (in characters; -1 = no limit)
on_activate() -> Unitno-opEnter callback (no parameter; use get_text to read the text)
on_text_change() -> Unitno-opContent change callback (same as above)
MethodPurpose
get_text() / set_text(t)Read / set text
on_activate(fn())Enter callback
on_text_change(fn())Content change callback

TextEdit ​

moonbit
let t = @yue.TextEdit::make(text="正文")
t.on_text_change(fn() { sync(t.get_text()) })
ParameterTypeDefaultDescription
textString""Initial text
on_text_change() -> Unitno-opContent change callback
MethodPurpose
get_text() / set_text(t)Read / set full text
undo() / redo() / can_undo() / can_redo()Undo and redo
cut() / copy() / paste()Clipboard editing
select_all() / select_range(start, end)Selection
get_text_in_range(start, end)Read text in range
insert_text(t) / insert_text_at(t, pos)Insert
delete() / delete_range(start, end)Delete
get_text_bounds_height()Actual height of the text (for auto-height layouts)
on_text_change(fn())Content change callback

Slider ​

moonbit
let s = @yue.Slider::make(value=0.0, range=Some((0.0, 100.0)), step=Some(1.0))
s.on_value_change(fn() { update(s.get_value()) })
ParameterTypeDefaultDescription
valueDouble0.0Initial value
range(Double, Double)?NoneRange (min, max)
stepDouble?NoneStep size
on_value_change() -> Unitno-opValue change callback (no parameter; use get_value to read the value)
MethodPurpose
get_value() / set_value(v)Read / set current value
set_range(min, max) / set_step(d)Range / step size
on_value_change(fn())Value change callback
on_sliding_complete(fn())Drag-completed callback

ProgressBar ​

moonbit
let p = @yue.ProgressBar::make(value=0.43)
ParameterTypeDefaultDescription
valueDouble0.0Initial value, in 0..1
indeterminateBoolfalseBack-and-forth scrolling mode
MethodPurpose
set_value(v)Set value (0..1)
set_indeterminate(b)Back-and-forth scrolling mode

Picker ​

moonbit
let p = @yue.Picker::make(items=["甲", "乙", "丙"], selected=0)
p.on_selection_change(fn() { refresh(p.get_selected_item_index()) })
ParameterTypeDefaultDescription
itemsArray[String][]Option list
selectedInt0Initially selected index
on_selection_change() -> Unitno-opSelection change callback
MethodPurpose
add_item(t) / remove_item_at(i) / clear()Maintain options
select_item_at(i)Select
get_selected_item() / get_selected_item_index()Read selected item
on_selection_change(fn())Selection change callback

ComboBox (editable) ​

moonbit
let c = @yue.ComboBox::make(items=["红", "绿"])
c.on_text_change(fn() { refresh(c.get_text()) })
ParameterTypeDefaultDescription
itemsArray[String][]Option list
selectedInt0Initially selected index
on_selection_change() -> Unitno-opOption change callback
on_text_change() -> Unitno-opEdit field text change callback
MethodPurpose
add_item(t) / select_item_at(i) / get_selected_item()Same as Picker
get_text() / set_text(t)Read / set edit field text
on_selection_change(fn()) / on_text_change(fn())The two change callbacks

DatePicker ​

moonbit
let d = @yue.DatePicker::make(epoch=Some(1700000000L))
d.on_date_change(fn() { show(d.get_date()) })
ParameterTypeDefaultDescription
epochInt64?NoneInitial date (Unix epoch seconds)
on_date_change() -> Unitno-opDate change callback
MethodPurpose
get_date() / set_date(epoch_seconds)Read / set date (epoch seconds)
on_date_change(fn())Date change callback

For customizations such as hiding the steppers, use DatePicker::new_with(DatePickerOptions).

Group / Scroll / Separator ​

moonbit
let g = @yue.Group::make("标题", content_view)
let sc = @yue.Scroll::make(content_view, policy=Some((Automatic, Automatic)))
let sep = @yue.Separator::make(Horizontal)   // or Vertical

Group::make parameters:

ParameterTypeDefaultDescription
titleStringrequiredTitle
contentT : ViewLikerequiredContent view (set via set_content internally)

Scroll::make parameters:

ParameterTypeDefaultDescription
contentT : ViewLikerequiredContent view
content_size(Double, Double)?NoneContent size; if omitted, the whole page scrolls following the content's natural height
policy(ScrollPolicy, ScrollPolicy)?NoneNative scrollbar policy (horizontal, vertical): Always / Never / Automatic; when passed explicitly the native scrollbar is kept
overlayBooltrueScrollbar form: on Linux/macOS requests the platform's overlay style; on Windows the default form is a self-drawn floating thumb (overlay=false or an explicit policy keeps native classic bars)

Separator::make parameters: orientation : Orientation = Horizontal (Horizontal / Vertical).

MethodPurpose
Group::set_title(t)Change title
Scroll::set_content(v)Replace content
Scroll::set_content_size(w, h)Explicitly set content size
Scroll::refresh_content_size()Recompute the scroll range from the current content (call it when the content is mounted only after set_content, or grows noticeably taller)
Scroll::set_scroll_position(h, v)Set scroll position
Scroll::set_scrollbar_policy(h, v) / set_overlay_scrollbar(b)Scrollbars
Scroll::get_scroll_position_x/y() / get_max_scroll_position_x/y()Read position and max scroll amount
Scroll::on_scroll(fn() -> Bool)Scroll position changed (wheel / drag / programmatic; return true to intercept)

Hover Group (HoverGroup) ​

Hovering anywhere in the group — including all child widgets — puts the whole group into the hover state: events triggered on child widgets change the group's styling too.

moonbit
@declarative.hover_group(
  @declarative.vbox([ /* card content: title / buttons / inputs */ ], style=[("padding", 14.0)]),
  radius=8.0,
  on_change=fn(h, host) {
    // the background highlight is built in; extra feedback like the cursor
    // is applied to host in the callback
    @yue.View::set_cursor(host, @yue.Cursor::new(if h { @yue.Hand } else { @yue.Default }))
  },
)
ParameterTypeDefaultDescription
childNoderequiredGroup content
hover_bg / base_bgStringtheme fill_hover / empty (transparent)Hover / idle background (self-drawn)
radiusDouble0Background corner radius
on_change(Bool, Container) -> Unitno-opFired on hover-state flips only, receives the group container
stylearray[]Group container layout styles

The detection mechanism is pointer-position polling (100ms, comparing the global pointer coordinates against the group's screen rectangle) and does not rely on the container's enter/leave — under GTK, child containers' and native widgets' event windows monopolize pointer events, so the container layer never sees enter; the same mechanism works on Windows with cross-platform-identical behavior.

Overlay Scrollbar (OverlayScroll) ​

On Windows, scroll()'s default form (overlay=true with no explicit policy) already routes through this self-drawn floating thumb; this component is the explicitly opted-in entry point — use it when you want the same self-drawn floating thumb on Linux/macOS (instead of the platform overlay style), unified across all three platforms. Behavior: hides the platform scrollbar and draws a themed slim bar over the content's right edge — appears on scroll / hover, fades out in two steps after about 1s of inactivity, draggable along the track, wheel events on the slim bar forwarded to content scrolling.

Standalone use (when the content is already a Node):

moonbit
@declarative.overlay_scroll(
  @declarative.vbox([@declarative.label("Line 1"), /* ... */]),
  style=[("height", 180.0)],   // container style; height comes from outer layout or is explicit
)
ParameterTypeDefaultDescription
contentNoderequiredScroll content (declarative node)
stylearray[]Outer container styles
handle(Scroll) -> Unitno-opAccess the inner Scroll for programmatic scrolling

Tab ​

moonbit
let t = @yue.Tab::make(pages=[("第一页", page1), ("第二页", page2)])
t.on_selected_page_change(fn() { switch_to(t.get_selected_page_index()) })
ParameterTypeDefaultDescription
pagesArray[(String, Container)][]Page titles and contents (usually one Container per page)
on_change() -> Unitno-opPage switch callback
MethodPurpose
add_page(title, v) / remove_page(v)Maintain pages
select_page_at(i) / get_selected_page_index() / page_count()Selection and queries
on_selected_page_change(fn())Page switch callback

Each page's container is the root of an independent yoga subtree; for building pages declaratively, see the tab node in docs/declarative.md.

Table ​

moonbit
let t = @yue.Table::new()
t.add_column_text("Name", 120)
t.add_column_checkbox("Enabled", 60)
t.set_model(my_model, column_count=2)   // model see below

Column types: add_column_text(title, width) / add_column_edit(title, width) (edit results are written back to the model via set_value) / add_column_checkbox(title, width) (toggles are written back to the model via set_value) / add_column_custom(title, width, draw) (draw receives the Painter, the cell rect, and the ColorText text/color from the model, drawing each cell by hand). For full column options use add_column_with_options(title, ColumnOptions).

The data model goes through a MoonBit trait bridge; no matter how many rows, values are fetched on demand:

moonbit
trait TableModel {
  fn row_count(Self) -> Int
  fn get_value(Self, column : Int, row : Int) -> TableValue   // Str / Flag / ColorText
  fn set_value(Self, column : Int, row : Int, value : TableValue) -> Unit
}
MethodPurpose
set_model(m, column_count)Attach a data model
on_row_activate(fn(row)) / on_selection_change(fn()) / on_toggle_checkbox(fn(column, row))Row activation / selection change / checkbox toggle
enable_multiple_selection(b) / select_row(i) / get_selected_row()Multiple selection and selected row
set_has_border(b)Border
notify_row_insertion(i) / notify_row_deletion(i) / notify_value_change(row, col)The three refresh methods after model changes

Canvas and Images ​

Custom drawing on any view:

moonbit
view.on_draw(fn(painter) {
  painter.set_fill_color("#FF8800")
  painter.fill_rect(0.0, 0.0, 80.0, 80.0)
  painter.set_blend_mode(@yue.Multiply)   // 25 blend modes
})

Common Painter methods:

MethodPurpose
set_fill_color(hex) / set_stroke_color(hex)Colors
fill_rect / stroke_rect / clip_rect(x, y, w, h)Rectangle drawing / clipping
begin_path / close_path / move_to / line_to / arc / bezier_curve_toPaths
fill() / stroke()Commit path
save() / restore() / translate / scale / rotateTransforms
draw_text(...) / draw_attributed_text(...)Text
draw_image(...) / draw_image_from_rect(...)Images
draw_canvas(...) / draw_canvas_from_rect(...)Offscreen canvas
set_blend_mode(m)Blend mode (BlendMode)

Images and offscreen bitmaps:

moonbit
let img = @yue.Image::new_from_file("a.png")
let slot = @yue.ImageSlot::new()   // the widget that displays the image
slot.set(Some(img))
APIPurpose
Image::new_from_file(path) / new_from_png(bytes, scale?)Loading
img.resize(w, h, scale?) / get_width() / get_height()Scaling and dimensions
img.write_to_file(format, path)Export
img.is_empty() / get_scale_factor()State
Canvas::new(w, h) + get_painter()Offscreen bitmap
ImageSlot::new() + set(img?) / get()Image display widget

Attributed text:

moonbit
let at = @yue.AttributedText::new("一段文本", wrap=true, ellipsis=false)
at.set_color_for("#FF0000", 0, 2)
APIPurpose
AttributedText::new(text, align?, valign?, wrap?, ellipsis?)Creation
set_font_for(font, start, end) / set_color_for(hex, start, end)Set attributes per range (consistent across platforms)
set_font(f) / set_color(hex) / set_text / set_formatWhole-text attributes
get_bounds_for(w, h)Layout bounding box
Font::new(name, size, weight?, style?)Font

Animated Images GifPlayer ​

moonbit
let g = @yue.GifPlayer::make(image=Some(img))
g.set_animating(true)
ParameterTypeDefaultDescription
imageImage?NoneInitial image (GIF animation)
scaleImageScaleDownScale policy: None / Fill / Down / UpOrDown
MethodPurpose
set_image(img)Replace image
set_scale(s) / get_scale()Scale policy
set_animating(b) / is_animating()Play / pause
is_playing() / stop_animation_timer()Playback state / stop

Browser ​

moonbit
// Browser lives in the standalone yue/browser package (moon.pkg import "NoahLiu/moonbit-libyue/yue/browser")
let b = @browser.Browser::make(url="https://example.com")   // or html="<h1>本地</h1>"
ParameterTypeDefaultDescription
urlString""URL to load
htmlString""HTML to load; if both url and html are given, url wins
MethodPurpose
load_url(u) / load_html(html, base_url?)Loading
get_url() / get_title() / reload() / stop()Current URL / page title / reload / stop
go_back() / go_forward() / can_go_back() / can_go_forward()Navigation
is_loading()Loading state
set_user_agent(s)UA
execute_javascript(code) / execute_javascript_with_result(code, fn(ok, json))Execute JS; the latter fetches the result asynchronously (ok=success, json=result JSON text)
add_raw_binding(name, fn(json)) / remove_binding(name) / has_bindings()JS↔native bindings (when the page calls name(...), it receives JSON argument text)
register_protocol(scheme, fn(url) -> (mime, content)?)Custom protocol (return None to refuse)
unregister_protocol(scheme)Unregister protocol
get_cookies_for_url(url, fn(cookies))Query cookies
on_change_loading / on_update_title / on_update_command / on_commit_navigation / on_finish_navigationEvents

For customization options, use Browser::new_with_options(BrowserOptions).

Since 0.5.0 Browser lives in the standalone package NoahLiu/moonbit-libyue/yue/browser (API unchanged; @browser.Browser becomes @browser.Browser); apps that don't import it no longer link WebKit/WebView2.

Clipboard ​

moonbit
let clip = @yue.Clipboard::get()
clip.set_text("文本")
MethodPurpose
Clipboard::get()Default clipboard
set_text(t) / get_text()Text
set_data(kind, t) / get_data(kind) / set_data_image(img)Structured data
clear()Clear

Notification / Notification Center ​

moonbit
let n = @yue.Notification::new()
n.set_title("标题"); n.set_body("正文")
n.show()
MethodPurpose
set_title(t) / set_body(s)Content
set_silent(b)Silent
set_actions([(id, title)])Buttons (used with NotificationCenter's action callbacks)
show()Send (on Linux this is mandatory; see docs/adaptation.md)
close()Close
NotificationCenter::get() + add(n)Send via the notification center

MessageBox ​

moonbit
let box = @yue.MessageBox::new(Information)
box.add_button("好", 1)
box.on_response(fn(response) { ... })
box.show_for_window(win)
MethodPurpose
MessageBox::new(type_)Type (MessageBoxType)
set_title / set_text / set_informative_textText
add_button(title, response)Custom buttons
on_response(fn(response))Button response (response is the button number)
show() / show_for_window(win) / close()Show (modal) / close

FileDialog ​

moonbit
let fd = @yue.FileDialog::new_open()
fd.set_filters("图片:png,jpg|全部:*")
if fd.run_for_window(win) { fd.get_result() }
MethodPurpose
new_open() / new_save()Open / save
set_filters("描述:扩展1,扩展2|描述2:扩展3")Filters (* matches all)
set_folder(path) / set_filename(name)Initial directory / filename
set_options(FILE_OPTION_PICK_FOLDERS | MULTI_SELECT | SHOW_HIDDEN)Option bit combination
run_for_window(win) -> BoolRun modally
get_result()Result path

File I/O: read_text_file(path) -> String?, read_binary_file(path) -> Bytes?, write_text_file(path, content) -> Bool.

moonbit
let mb = @yue.MenuBar::new()
let m = mb.add_menu("文件")
m.add_label_item("打开").on_click(fn() { ... })
win.set_menubar(mb)
APIPurpose
MenuBar::new() + add_menu(title) -> MenuMenu bar
Menu::new()Popup menu (with popup_at(x, y))
add_label_item(t) / add_check_item(t) / add_radio_item(t)Plain / checkbox / radio items
add_role_item(role) / add_submenu(title) / add_separator()System role items / submenu / separator
MenuItem::on_click(fn())Click callback
MenuItem::is_checked() / set_checked(b)Checked state
MenuItem::get_label() / set_label(t) / set_accelerator(s)Text and accelerator
Menu::item_count() / item_at(i); same for MenuBarIteration

Tray ​

moonbit
let tray = match @yue.Tray::new("icon.png") {
  Ok(t) => t
  Err(e) => ...   // backend missing or icon read failure
}
APIPurpose
Tray::is_supported()Whether the backend is available
Tray::new(icon_path) -> Result[Tray, TrayError]Create (with structured errors)
set_icon(path) / set_icon_name(name)Change icon (theme name is Linux SNI only)
set_title(t)No-op on platforms without this concept
set_tooltip(title, body)Hover tooltip (Linux SNI only)
on_click(fn())Click callback
set_menu(menu)Attach context menu
remove()Remove icon

On Linux the pure MoonBit yue/traybus backend is recommended (the unified Tray API selects it automatically); see docs/tray.md for details.

System Integration (single instance / autostart / power / session / network) ​

System-level capabilities for desktop apps — unified entry points and a unified error style (Err(Unsupported) plus xxx_supported() probes first). Full demos live on the showcase "System" page.

Single Instance & Re-Activation ​

moonbit
match @system.SingleInstance::acquire("org.example.MyApp") {
  Ok(Some(handle)) => {
    // this process is the first instance — keep starting up
    handle.on_activate(fn(args) {
      // second instance launched: its command line arrives here;
      // restore / raise the window
    })
  }
  Ok(None) => return  // an instance exists and was woken — exit
  Err(_) => ()        // single-instance unavailable; degrade or exit
}
APIPurpose
SingleInstance::acquire(app_id) -> Result[SingleInstance?, SingleInstanceError]Try to become the first instance (app_id must be a valid DBus bus name: dot-separated segments, starting with a letter/underscore)
handle.on_activate(cb : (Array[String]) -> Unit)Register the re-activation callback (receives the second instance's command line, including argv[0])
set_instance_window_title(title)Window title (Windows' raise fallback searches by title; renaming at runtime breaks the fallback)

Linux claims an app-specific name on the session bus; Windows uses a named mutex plus a message window.

Autostart ​

moonbit
let auto = match @system.Autostart::new("org.example.MyApp") {
  Ok(a) => a
  Err(_) => ...   // platform unsupported (macOS deferred)
}
auto.enable()     // after Ok: launched on next login / reboot
auto.disable()    // cancel (idempotent)
APIPurpose
Autostart::new(app_id) -> Result[Autostart, AutostartError]Handle (app_id follows the single-instance constraint)
Autostart::is_supported()Whether the platform supports autostart
handle.is_enabled() -> Result[Bool, AutostartError]Query current state
handle.enable() / disable() -> Result[Unit, AutostartError]Set / cancel (both idempotent)
handle.path() -> Result[String, AutostartError]Autostart entry file path (Linux .desktop)

Linux writes a .desktop into $XDG_CONFIG_HOME/autostart (exe path resolved via /proc/self/exe); Windows writes the HKCU Run key.

Opening External Things ​

APIPurpose
open_url(url) -> Result[Unit, OpenUrlError]Hand a URL to the default browser
reveal_in_file_manager(path) -> Result[Unit, FileManagerError]Open and select in the file manager (relative paths resolve against the working directory; on Linux without FileManager1 it falls back to opening the parent directory — selection lost)

Ok only means "handed to the system"; the platform-side outcome does not travel back (whether the browser truly opens is the desktop's call).

Keep-Awake & User Idle ​

moonbit
match @system.KeepAwake::enable("org.example.MyApp") {
  Ok(k) => { /* display kept on */ ignore(k.release()) }  // release
  Err(_) => ()
}
match @system.idle_seconds() {
  Ok(sec) => ...   // seconds since the last input (threshold is the caller's)
  Err(_) => ()
}
APIPurpose
KeepAwake::is_supported()Whether the inhibition service / system capability is available
KeepAwake::enable(app_id) -> Result[KeepAwake, KeepAwakeError]Request keep-awake (Ok means the system accepted only; actual dimming behavior is platform policy)
handle.release()Release (idempotent)
keep_awake_active()Whether keep-awake is currently held
idle_supported() / idle_seconds() -> Result[Double, IdleError]User idle seconds (Linux X11; Wayland sessions report Unsupported explicitly)

Battery & Power Events ​

APIPurpose
battery_supported() / battery_query() -> Result[BatteryInfo?, PowerError]Battery reading (percent / charging / seconds-to-full and seconds-to-empty; no battery gives Ok(None))
power_source() -> Result[PowerSource, PowerError]Query the current power source (a desktop without battery is always AC)
power_event_supported() / on_power_source_change(cb)AC/battery switch events (PowerSource::Ac / OnBattery; Windows event wiring is deferred — probe supported() first)
suspend_resume_supported() / on_suspend_resume(cb)Suspend/resume events (SleepEvent::Suspending / Resuming; rapid suspend-resume cycles may deliver two Resuming events — no library-side debounce)

Battery goes through UPower (system bus) on Linux and GetSystemPowerStatus on Windows; suspend/resume goes through logind PrepareForSleep on Linux and power broadcasts on Windows.

Screen Lock ​

APIPurpose
session_lock_supported()Whether available (desktops whose locker bypasses logind deliver no signal)
session_lock_watch(cb) -> Result[Unit, SystemError]Subscribe to lock/unlock (SessionLockEvent::Locked / Unlocked; failures are structured errors, never silent)

Network Online Status ​

APIPurpose
network_supported() / network_status() -> Result[NetworkStatus, NetworkError]Current online status (Online = internet-reachable; captive portals count as Offline)
on_network_status_change(cb)Change events (dispatched on change only; Linux signal-driven, Windows polls every 5 seconds)

Linux goes through NetworkManager (system bus). The first network_status() builds the bus cache synchronously with a worst-case 1.5s block — inside callbacks and timers use events or the cache instead of repeated queries.

Popover ​

moonbit
let pop = @yue.Popover::new()
pop.set_content(view)
pop.show_relative_to(anchor_view)
MethodPurpose
set_content(v) / set_content_size(w, h)Content and size
show_relative_to(v)Pop up near the target view
close() / on_close(fn())Close

Global Shortcuts / Cursor / System ​

APIPurpose
register_global_shortcut("CmdOrCtrl+Shift+M", fn()) -> IntRegister; returns id; -1 means already taken (use another key)
unregister_global_shortcut(id)Unregister
Cursor::new(type_) + view.set_cursor(c)Cursor (CursorType)
Appearance::is_dark()Dark appearance
locale()Locale
Screen::scale_factor() / primary_size()Scale / primary screen size
App::set_name(s) / App::get_name()Application name
desktop_environment()Desktop environment name (for diagnostics)

Events (common to all widgets) ​

All widgets (ViewLike) support:

MethodPurpose
on_mouse_down / up / move / enter / leaveMouse
on_key_down / upKeyboard
on_size_changedSize changes
set_capture() / release_capture() / has_capture()Mouse capture
set_style(k, v) / set_style_str(k, v)Layout styles (runtime primitives)
Drag registration and drop callbacksDrag and drop (receivers must also register handle_drag_update returning allowed operations; without it every drag is rejected; the initiator's do_drag_file_paths / do_drag_data_full must be called inside the on_mouse_down callback to take effect; demo in the components example, "Windows & Web" page)

Event payload fields:

StructFields
MouseEventkind, button (1=left 2=right 3=middle), view_x/view_y (relative to view), window_x/window_y (relative to window), screen_x/screen_y (global screen coordinates, use directly for event-position popups like context menus), modifiers, timestamp
KeyEventkind, code (VKEY_* constants), modifiers, timestamp

modifiers bits: 1=Shift 2=Ctrl 4=Alt 8=Meta; KeyEvent::describe() outputs strings like "Ctrl+A". The key code constant table is unified across platforms (Windows VK codes are normalized at the event entry point), see yue/events.mbt; for click counting use ClickTracker (default 400ms / 5px).


Inherent Pitfalls ​

Caused by upstream libyue or platform behavior; read before using the corresponding APIs:

  1. Browser navigation rewrites the window title (Windows): after the WebView2 host control loads a page, it syncs the page's <title> to the host window title (no such behavior on Linux/GTK; verified 2026-09-19). Tools that rely on window titles for window management are affected; use on_update_title to manage the title display yourself when needed.
  2. Initial callback for Checkbox/Radio: a Checkbox/Radio created with checked=true will asynchronously receive one callback after mounting and entering the event loop (GTK toggled signal semantics). If callback logic depends on state, check is_checked() first, or tolerate this initial notification.
  3. Radio group switching is a double notification: when a new item is selected, the deselected old item also receives a callback (at which point the old item's is_checked()==false). Just handle the business based on "the newly selected one".
  4. Virtual key codes use the GTK table: VKEY_ESCAPE = 0xFF1B (65307), not the Windows VK value; letters and digits match ASCII. Do not mix the two tables in cross-platform code.
  5. Style key parsing rules: key names keep only ASCII letters and lowercase them, so flexDirection / flex-direction / flexdirection are equivalent; digits, hyphens and all other symbols are dropped, so do not use special characters in key names. See docs/layout.md for the full key list.
  6. Callbacks are kept alive automatically, but do not synchronously pump the event loop inside a callback: closures registered via on_* are held by the library with strong references; the same applies to Store subscriptions. After calling termination flows like @yue.quit() inside a callback, do not touch widgets anymore.
  7. Platform-specific APIs not wrapped: Toolbar / Vibrant (no symbols in the Linux static library), Button styles and ControlSize, Scroll bounce, App activation policy, Browser zoom, Image template images (macOS), ShortcutOptions / Lifetime::Reply / notification COMServerOptions (Windows), etc.; see docs/adaptation.md for the complete list.