Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
84 changes: 47 additions & 37 deletions appkit/ui_macos_darwin.v
Original file line number Diff line number Diff line change
Expand Up @@ -1007,14 +1007,14 @@ fn render_element(parent NativeView, el Element, key string, mut active map[stri
doc_h := content_height(el.children) + 16
mut doc := st.nodes[doc_key] or { native_nil_view() }
if native_is_nil(doc) {
doc = native_new_flipped_view(native_rect(0, 0, el.frame.width, doc_h), el.box.bg)
doc = native_new_flipped_view(native_rect(0, 0, el.frame.width, doc_h), el.box)
st.nodes[doc_key] = doc
st.node_kinds[doc_key] = .view
native_set_document_view(native, doc)
macos.release(doc)
} else {
native_set_frame(doc, native_rect(0, 0, el.frame.width, doc_h))
native_set_background(doc, el.box.bg)
native_set_box_background(doc, el.box)
}
render_children(doc, el.children, doc_key, mut active)
}
Expand Down Expand Up @@ -1158,7 +1158,7 @@ fn native_create_element(el Element) NativeView {
native_new_view(element_rect(el.frame), el.box, element_interactive(el))
}
.scroll {
native_new_scroll(element_rect(el.frame), el.box.bg, el.persistent_scrollbars)
native_new_scroll(element_rect(el.frame), el.box, el.persistent_scrollbars)
}
.label {
native_new_label(element_rect(el.frame), el.text, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.italic, el.text_style.underline, align_value(el.text_style.align), el.text_style.lines)
Expand All @@ -1167,7 +1167,7 @@ fn native_create_element(el Element) NativeView {
native_new_image(element_rect(el.frame), el.image_path, el.rotation)
}
.button {
native_new_button(element_rect(el.frame), el.text, el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.italic, el.text_style.underline, el.box.radius, el.text_style.lines, el.image_path, el.native_style)
native_new_button(element_rect(el.frame), el.text, el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.italic, el.text_style.underline, el.text_style.lines, el.image_path, el.native_style)
}
.checkbox {
native_new_checkbox(el)
Expand Down Expand Up @@ -1203,7 +1203,7 @@ fn native_update_element(native NativeView, el Element, declared_text_changed bo
}
.scroll {
native_set_frame(native, element_rect(el.frame))
native_set_scroll_background(native, el.box.bg)
native_set_scroll_background(native, el.box)
native_set_scrollbar_mode(native, el.persistent_scrollbars)
}
.label {
Expand All @@ -1213,7 +1213,7 @@ fn native_update_element(native NativeView, el Element, declared_text_changed bo
native_update_image(native, element_rect(el.frame), el.image_path, el.rotation)
}
.button {
native_update_button(native, element_rect(el.frame), el.text, el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.italic, el.text_style.underline, el.box.radius, el.text_style.lines, el.image_path, el.native_style)
native_update_button(native, element_rect(el.frame), el.text, el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.italic, el.text_style.underline, el.text_style.lines, el.image_path, el.native_style)
}
.checkbox {
native_update_checkbox(native, el)
Expand All @@ -1229,7 +1229,7 @@ fn native_update_element(native NativeView, el Element, declared_text_changed bo
}
.text_field {
native_update_text_field(native, element_rect(el.frame), el.placeholder, el.text,
el.box.bg, el.text_style.color, el.text_style.size, el.box.radius,
el.box, el.text_style.color, el.text_style.size,
declared_text_changed, el.readonly, el.enabled)
}
.text_area {
Expand Down Expand Up @@ -1687,9 +1687,9 @@ fn native_set_associated_object(obj NativeView, key voidptr, value NativeView) {
macos.set_associated_object(obj, key, value, macos.assoc_retain_nonatomic)
}

fn native_new_flipped_view(frame NativeRect, bg u32) NativeView {
fn native_new_flipped_view(frame NativeRect, box BoxStyle) NativeView {
native := macos.msg_id_rect(macos.alloc('UI2FlippedView'), 'initWithFrame:', appkit_rect(frame))
native_set_background(native, bg)
native_set_box_background(native, box)
return native
}

Expand All @@ -1701,12 +1701,11 @@ fn native_new_view(frame NativeRect, box BoxStyle, interactive bool) NativeView
return native
}

fn native_new_scroll(frame NativeRect, bg u32, persistent_scrollbars bool) NativeView {
fn native_new_scroll(frame NativeRect, box BoxStyle, persistent_scrollbars bool) NativeView {
scroll_view := macos.msg_id_rect(macos.alloc('NSScrollView'), 'initWithFrame:', appkit_rect(frame))
macos.msg_void_bool(scroll_view, 'setHasVerticalScroller:', true)
native_set_scrollbar_mode(scroll_view, persistent_scrollbars)
macos.msg_void_bool(scroll_view, 'setDrawsBackground:', true)
native_set_scroll_background(scroll_view, bg)
native_set_scroll_background(scroll_view, box)
return scroll_view
}

Expand All @@ -1720,9 +1719,12 @@ fn native_set_document_view(scroll NativeView, view NativeView) {
macos.msg_void1(scroll, 'setDocumentView:', view)
}

fn native_set_scroll_background(scroll NativeView, bg u32) {
macos.msg_void_bool(scroll, 'setDrawsBackground:', true)
macos.msg_void1(scroll, 'setBackgroundColor:', native_color(bg))
fn native_set_scroll_background(scroll NativeView, box BoxStyle) {
draws_background := box_draws_fill(box)
macos.msg_void_bool(scroll, 'setDrawsBackground:', draws_background)
if draws_background {
macos.msg_void1(scroll, 'setBackgroundColor:', native_color(box.bg))
}
}

fn native_new_image(frame NativeRect, path string, rotation f64) NativeView {
Expand Down Expand Up @@ -1777,17 +1779,18 @@ fn native_update_label(label_view NativeView, frame NativeRect, text string, tex
macos.msg_void_bool(cell, 'setUsesSingleLineMode:', lines == 1)
}

fn native_new_button(frame NativeRect, title string, bg_hex u32, text_hex u32, size f64, bold bool, italic bool, underline bool, radius f64, lines int, image_name string, native_style bool) NativeView {
fn native_new_button(frame NativeRect, title string, box BoxStyle, text_hex u32, size f64, bold bool, italic bool, underline bool, lines int, image_name string, native_style bool) NativeView {
button_view := macos.msg_id_rect(macos.alloc('NSButton'), 'initWithFrame:', appkit_rect(frame))
native_update_button(button_view, frame, title, bg_hex, text_hex, size, bold, italic, underline, radius, lines, image_name, native_style)
native_update_button(button_view, frame, title, box, text_hex, size, bold, italic, underline,
lines, image_name, native_style)
return button_view
}

fn native_update_button(button_view NativeView, frame NativeRect, title string, bg_hex u32, text_hex u32, size f64, bold bool, italic bool, underline bool, radius f64, lines int, image_name string, native_style bool) {
fn native_update_button(button_view NativeView, frame NativeRect, title string, box BoxStyle, text_hex u32, size f64, bold bool, italic bool, underline bool, lines int, image_name string, native_style bool) {
native_set_frame(button_view, frame)
macos.msg_void1(button_view, 'setTitle:', macos.nsstring(title))
macos.msg_void_u64(button_view, 'setBezelStyle:', 1)
if native_style {
if native_style && box_draws_fill(box) {
macos.msg_void_i64(button_view, 'setButtonType:', ns_button_type_momentary_push_in)
macos.msg_void_bool(button_view, 'setBordered:', true)
macos.msg_void_bool(button_view, 'setWantsLayer:', false)
Expand All @@ -1796,8 +1799,8 @@ fn native_update_button(button_view NativeView, frame NativeRect, title string,
macos.msg_void_bool(button_view, 'setBordered:', false)
macos.msg_void1(button_view, 'setFont:', native_font(size, bold, italic))
native_control_set_attributed_title(button_view, title, text_hex, size, bold, italic, underline)
native_set_background(button_view, bg_hex)
native_set_corner_radius(button_view, radius)
native_set_box_background(button_view, box)
native_set_corner_radius(button_view, box.radius)
}
cell := macos.msg_id(button_view, 'cell')
macos.msg_void_i64(cell, 'setLineBreakMode:', 4)
Expand Down Expand Up @@ -1839,18 +1842,18 @@ fn native_update_switch_control(view NativeView, el Element) {
}

fn native_new_toggle_button(el Element) NativeView {
native := native_new_button(element_rect(el.frame), el.text, el.box.bg, el.text_style.color,
native := native_new_button(element_rect(el.frame), el.text, el.box, el.text_style.color,
el.text_style.size, el.text_style.bold, el.text_style.italic, el.text_style.underline,
el.box.radius, el.text_style.lines, el.image_path, el.native_style)
el.text_style.lines, el.image_path, el.native_style)
native_update_toggle_button(native, el)
return native
}

fn native_update_toggle_button(native NativeView, el Element) {
box := if el.checked { el.toggle_down_box } else { el.box }
style := if el.checked { el.toggle_down_text_style } else { el.text_style }
native_update_button(native, element_rect(el.frame), el.text, box.bg, style.color, style.size,
style.bold, style.italic, style.underline, box.radius, style.lines, el.image_path,
native_update_button(native, element_rect(el.frame), el.text, box, style.color, style.size,
style.bold, style.italic, style.underline, style.lines, el.image_path,
el.native_style)
macos.msg_void_i64(native, 'setButtonType:', ns_button_type_push_on_push_off)
macos.msg_void_i64(native, 'setState:', if el.checked { i64(1) } else { i64(0) })
Expand Down Expand Up @@ -1920,20 +1923,20 @@ fn native_update_dropdown(popup NativeView, el Element) {
}
native_select_dropdown_item(popup, el.text)
macos.msg_void1(popup, 'setFont:', native_font(el.text_style.size, el.text_style.bold, el.text_style.italic))
macos.msg_void_bool(popup, 'setBordered:', true)
macos.msg_void_bool(popup, 'setBordered:', box_draws_fill(el.box))
macos.msg_void_u64(popup, 'setBezelStyle:', 1)
}

fn native_new_text_field(el Element) NativeView {
frame := element_rect(el.frame)
cls := if el.secure { 'NSSecureTextField' } else { 'NSTextField' }
field := macos.msg_id_rect(macos.alloc(cls), 'initWithFrame:', appkit_rect(frame))
native_update_text_field(field, frame, el.placeholder, el.text, el.box.bg, el.text_style.color,
el.text_style.size, el.box.radius, true, el.readonly, el.enabled)
native_update_text_field(field, frame, el.placeholder, el.text, el.box, el.text_style.color,
el.text_style.size, true, el.readonly, el.enabled)
return field
}

fn native_update_text_field(field NativeView, frame NativeRect, placeholder string, text string, bg_hex u32, text_hex u32, size f64, _radius f64, declared_text_changed bool, readonly bool, enabled bool) {
fn native_update_text_field(field NativeView, frame NativeRect, placeholder string, text string, box BoxStyle, text_hex u32, size f64, declared_text_changed bool, readonly bool, enabled bool) {
native_set_frame(field, frame)
// Unrelated refreshes preserve native edits. A changed declaration remains
// controlled and is applied explicitly.
Expand All @@ -1946,8 +1949,11 @@ fn native_update_text_field(field NativeView, frame NativeRect, placeholder stri
macos.msg_void_bool(field, 'setBordered:', true)
macos.msg_void_bool(field, 'setBezeled:', true)
macos.msg_void_u64(field, 'setBezelStyle:', 1)
macos.msg_void_bool(field, 'setDrawsBackground:', true)
macos.msg_void1(field, 'setBackgroundColor:', native_color(bg_hex))
draws_background := box_draws_fill(box)
macos.msg_void_bool(field, 'setDrawsBackground:', draws_background)
if draws_background {
macos.msg_void1(field, 'setBackgroundColor:', native_color(box.bg))
}
macos.msg_void_bool(field, 'setEditable:', !readonly && enabled)
macos.msg_void_bool(field, 'setSelectable:', true)
macos.msg_void_bool(field, 'setEnabled:', enabled)
Expand All @@ -1964,6 +1970,7 @@ fn native_new_text_area(el Element) NativeView {
scroll_view := macos.msg_id_rect(macos.alloc('NSScrollView'), 'initWithFrame:', appkit_rect(frame))
macos.msg_void_bool(scroll_view, 'setHasVerticalScroller:', true)
macos.msg_void_bool(scroll_view, 'setAutohidesScrollers:', true)
native_set_scroll_background(scroll_view, el.box)
tv := native_new_text_view(macos.rect(0, 0, frame.width, frame.height), el)
macos.msg_void1(scroll_view, 'setDocumentView:', tv)
macos.release(tv)
Expand All @@ -1979,8 +1986,8 @@ fn native_new_text_view(frame macos.Rect, el Element) NativeView {
macos.msg_void_bool(tv, 'setVerticallyResizable:', true)
macos.msg_void_bool(tv, 'setHorizontallyResizable:', false)
macos.msg_void_u64(tv, 'setAutoresizingMask:', 2) // NSViewWidthSizable
macos.msg_void_bool(tv, 'setDrawsBackground:', !el.box.transparent)
if !el.box.transparent {
macos.msg_void_bool(tv, 'setDrawsBackground:', box_draws_fill(el.box))
if box_draws_fill(el.box) {
macos.msg_void1(tv, 'setBackgroundColor:', native_color(el.box.bg))
}
macos.msg_void1(tv, 'setTextColor:', native_color(el.text_style.color))
Expand All @@ -1999,6 +2006,7 @@ fn native_update_text_area(native NativeView, el Element, declared_text_changed
if !el.disable_scroll {
macos.msg_void_bool(native, 'setHasVerticalScroller:', true)
macos.msg_void_bool(native, 'setAutohidesScrollers:', true)
native_set_scroll_background(native, el.box)
}
tv := text_area_text_view(native, el.disable_scroll)
if native_is_nil(tv) {
Expand All @@ -2008,8 +2016,8 @@ fn native_update_text_area(native NativeView, el Element, declared_text_changed
macos.msg_void_bool(tv, 'setSelectable:', true)
macos.msg_void1(tv, 'setFont:', native_text_style_font(el.text_style))
macos.msg_void1(tv, 'setTextColor:', native_color(el.text_style.color))
macos.msg_void_bool(tv, 'setDrawsBackground:', !el.box.transparent)
if !el.box.transparent {
macos.msg_void_bool(tv, 'setDrawsBackground:', box_draws_fill(el.box))
if box_draws_fill(el.box) {
macos.msg_void1(tv, 'setBackgroundColor:', native_color(el.box.bg))
}
current := macos.utf8_string(macos.msg_id(tv, 'string'))
Expand Down Expand Up @@ -2133,7 +2141,7 @@ fn native_set_background(view NativeView, hex u32) {
}

fn native_set_box_background(view NativeView, box BoxStyle) {
if !box.transparent {
if box_draws_fill(box) {
native_set_background(view, box.bg)
return
}
Expand Down Expand Up @@ -2243,7 +2251,9 @@ fn ui2_app_did_finish_launching(_self voidptr, _cmd voidptr, _notification voidp
// The app delegate doubles as window delegate for windowDidResize:
macos.msg_void1(st.window, 'setDelegate:', st.app_delegate)
root_frame := native_rect(0, 0, f64(st.run_config.width), f64(st.run_config.height))
st.root_view = native_new_flipped_view(root_frame, 0xffffff)
st.root_view = native_new_flipped_view(root_frame, BoxStyle{
bg: 0xffffff
})
native_register_drop_types(st.root_view)
native_set_content_view(st.window, st.root_view)
macos.release(st.root_view)
Expand Down
55 changes: 53 additions & 2 deletions appkit/ui_macos_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,10 @@ fn test_macos_native_style_button_keeps_appkit_bezel_and_press_state() {
defer {
macos.release(pool)
}
button_view := native_new_button(native_rect(0, 0, 96, 40), 'Count', 0x3478d4,
0xffffff, 15, true, false, false, 7, 1, '', true)
button_view := native_new_button(native_rect(0, 0, 96, 40), 'Count', BoxStyle{
bg: 0x3478d4
radius: 7
}, 0xffffff, 15, true, false, false, 1, '', true)
defer {
macos.release(button_view)
}
Expand All @@ -166,6 +168,55 @@ fn test_macos_native_style_button_keeps_appkit_bezel_and_press_state() {
assert !macos.msg_bool(button_view, 'wantsLayer')
}

fn test_macos_transparent_box_controls_disable_native_backgrounds() {
pool := macos.autorelease_pool_new()
defer {
macos.release(pool)
}
transparent_box := BoxStyle{
bg: 0xff00ff
transparent: true
}
button_view := native_new_button(native_rect(0, 0, 96, 40), 'Clear', BoxStyle{},
0xffffff, 15, false, false, false, 0, '', true)
scroll_view := native_new_scroll(native_rect(0, 0, 120, 80), BoxStyle{}, false)
toggle_view := native_new_toggle_button(toggle_button(
title: 'Clear toggle'
box: transparent_box
native_style: true
))
dropdown_view := native_new_dropdown(dropdown('clear-dropdown', 'One', ['One'], rect(0,
0, 120, 32), transparent_box, TextStyle{}))
field := native_new_text_field(text_field('clear-field', '', '', rect(0, 0, 120, 32),
transparent_box, TextStyle{}, keyboard_default))
text_area_view := native_new_text_area(text_area('clear-area', '', rect(0, 0, 120, 80),
transparent_box, TextStyle{}))
defer {
macos.release(button_view)
macos.release(scroll_view)
macos.release(toggle_view)
macos.release(dropdown_view)
macos.release(field)
macos.release(text_area_view)
}

native_update_button(button_view, native_rect(0, 0, 96, 40), 'Clear', transparent_box,
0xffffff, 15, false, false, false, 0, '', true)
native_set_scroll_background(scroll_view, transparent_box)

assert !macos.msg_bool(button_view, 'isBordered')
assert macos.msg_bool(button_view, 'wantsLayer')
layer := macos.msg_id(button_view, 'layer')
assert native_is_nil(macos.msg_id(layer, 'backgroundColor'))
assert !macos.msg_bool(scroll_view, 'drawsBackground')
assert !macos.msg_bool(toggle_view, 'isBordered')
assert !macos.msg_bool(dropdown_view, 'isBordered')
assert !macos.msg_bool(field, 'drawsBackground')
assert !macos.msg_bool(text_area_view, 'drawsBackground')
text_view := text_area_text_view(text_area_view, false)
assert !macos.msg_bool(text_view, 'drawsBackground')
}

fn test_macos_text_field_uses_native_bezel_without_layer_mask() {
pool := macos.autorelease_pool_new()
defer {
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_window/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The header is an ordinary draggable ui2 `Rectangle`. Its pointer-down event is
handed to `performWindowDragWithEvent:` or `WM_NCLBUTTONDOWN`, so the operating
system still performs the move. The close button calls `ui2.quit()`. Everything
else—including the separated rounded surfaces and the clear space between
them—is declared in [`custom_window.qml`](custom_window.qml).
them—is declared in [`custom_window.vml`](custom_window.vml).

For a resizable custom frame, keep the same pattern and add narrow draggable
ui2 regions at the edges, routing them to the platform's native resize action.
2 changes: 1 addition & 1 deletion examples/custom_window/custom_window_darwin.v
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn macos_configure_custom_window() bool {
// small NSWindow subclass, so add the capability before dropping its frame.
window_class := macos.get_class('UI2Window')
if !macos_custom_window_is_nil(window_class) {
macos.add_method(window_class, 'canBecomeKey', voidptr(custom_window_can_become_key), 'B@:')
macos.add_method(window_class, 'canBecomeKeyWindow', voidptr(custom_window_can_become_key), 'B@:')
macos.add_method(window_class, 'canBecomeMainWindow', voidptr(custom_window_can_become_key), 'B@:')
}
macos.msg_void_u64(window, 'setStyleMask:', ns_window_style_borderless)
Expand Down
4 changes: 2 additions & 2 deletions examples/custom_window/custom_window_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ fn test_custom_window_counter_never_becomes_negative() {
assert app.completed == 0
}

fn test_custom_window_qml_exposes_clear_space_and_custom_chrome() {
fn test_custom_window_vml_exposes_clear_space_and_custom_chrome() {
app := CustomWindowDemo{
transparent_screen: true
screen_background: '#010203'
}
root := ui2.element_from_qml_model(custom_window_qml_source, app, ui2.rect(0, 0, custom_window_width, custom_window_height)) or { panic(err) }
root := ui2.element_from_vml_model(custom_window_vml_source, app, ui2.rect(0, 0, custom_window_width, custom_window_height)) or { panic(err) }
ui2.validate_element_tree(root) or { panic(err) }
assert root.box.transparent
drag := find_custom_window_element(root, 'window_drag') or { panic('missing drag surface') }
Expand Down
6 changes: 3 additions & 3 deletions examples/custom_window/main.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ui2

const custom_window_width = 440
const custom_window_height = 210
const custom_window_qml_source = $embed_file('custom_window.qml').to_string()
const custom_window_vml_source = $embed_file('custom_window.vml').to_string()

@[heap]
pub struct CustomWindowDemo {
Expand Down Expand Up @@ -68,8 +68,8 @@ fn custom_window_pointer(raw string) ?(string, string) {
fn build_custom_window_screen() ui2.Element {
mut state := unsafe { custom_window_state }
state.prepare_window()
return ui2.element_from_qml_model(custom_window_qml_source, *state, ui2.bounds()) or {
eprintln('custom-window QML failed: ${err}')
return ui2.element_from_vml_model(custom_window_vml_source, *state, ui2.bounds()) or {
eprintln('custom-window VML failed: ${err}')
ui2.screen(0x0f172a, [])
}
}
Expand Down
Loading
Loading