diff --git a/appkit/ui_macos_darwin.v b/appkit/ui_macos_darwin.v index 7317cad..ad24f36 100644 --- a/appkit/ui_macos_darwin.v +++ b/appkit/ui_macos_darwin.v @@ -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) } @@ -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) @@ -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) @@ -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 { @@ -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) @@ -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 { @@ -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 } @@ -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 } @@ -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 { @@ -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) @@ -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) @@ -1839,9 +1842,9 @@ 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 } @@ -1849,8 +1852,8 @@ fn native_new_toggle_button(el Element) NativeView { 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) }) @@ -1920,7 +1923,7 @@ 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) } @@ -1928,12 +1931,12 @@ 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. @@ -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) @@ -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) @@ -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)) @@ -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) { @@ -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')) @@ -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 } @@ -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) diff --git a/appkit/ui_macos_test.v b/appkit/ui_macos_test.v index ea345a6..cea826c 100644 --- a/appkit/ui_macos_test.v +++ b/appkit/ui_macos_test.v @@ -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) } @@ -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 { diff --git a/examples/custom_window/README.md b/examples/custom_window/README.md index ffc7fa7..d9371f7 100644 --- a/examples/custom_window/README.md +++ b/examples/custom_window/README.md @@ -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. diff --git a/examples/custom_window/custom_window.qml b/examples/custom_window/custom_window.vml similarity index 100% rename from examples/custom_window/custom_window.qml rename to examples/custom_window/custom_window.vml diff --git a/examples/custom_window/custom_window_darwin.v b/examples/custom_window/custom_window_darwin.v index 7c88865..4bf9977 100644 --- a/examples/custom_window/custom_window_darwin.v +++ b/examples/custom_window/custom_window_darwin.v @@ -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) diff --git a/examples/custom_window/custom_window_test.v b/examples/custom_window/custom_window_test.v index 0f4cc12..0ef1f93 100644 --- a/examples/custom_window/custom_window_test.v +++ b/examples/custom_window/custom_window_test.v @@ -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') } diff --git a/examples/custom_window/main.v b/examples/custom_window/main.v index 8904ede..3449a8f 100644 --- a/examples/custom_window/main.v +++ b/examples/custom_window/main.v @@ -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 { @@ -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, []) } } diff --git a/ui/ui.v b/ui/ui.v index 35085fa..f5e6f65 100644 --- a/ui/ui.v +++ b/ui/ui.v @@ -119,6 +119,12 @@ pub: border_bottom f64 } +// box_draws_fill is shared by native and custom renderers so every element +// backed by BoxStyle gives transparent the same meaning. +fn box_draws_fill(box BoxStyle) bool { + return !box.transparent +} + // box_border_width keeps a declared border inside its element. Border widths // are logical units, just like Rect and corner radii; each backend is // responsible for mapping those units to its native device scale. diff --git a/ui/ui_test.v b/ui/ui_test.v index f9ea233..c25e692 100644 --- a/ui/ui_test.v +++ b/ui/ui_test.v @@ -1,5 +1,13 @@ module ui2 +fn test_transparent_box_does_not_draw_a_fill() { + assert box_draws_fill(BoxStyle{}) + assert !box_draws_fill(BoxStyle{ + bg: 0xff00ff + transparent: true + }) +} + fn test_box_border_width_stays_inside_the_element() { assert box_border_width(-1, 20) == 0 assert box_border_width(0, 20) == 0 diff --git a/uikit/ui_ios.v b/uikit/ui_ios.v index a1620f2..265a459 100644 --- a/uikit/ui_ios.v +++ b/uikit/ui_ios.v @@ -306,8 +306,13 @@ fn font(size f64, bold bool) macos.Id { return macos.msg_id_f64(macos.get_class('UIFont'), 'systemFontOfSize:', size) } -fn set_background(view View, hex u32) { - macos.msg_void1(view, 'setBackgroundColor:', ios.color(hex)) +fn set_box_background(view View, box BoxStyle) { + color := if box_draws_fill(box) { + ios.color(box.bg) + } else { + macos.msg_id(macos.get_class('UIColor'), 'clearColor') + } + macos.msg_void1(view, 'setBackgroundColor:', color) } fn set_corner_radius(view View, radius f64) { @@ -478,17 +483,17 @@ fn set_scroll_content_offset_y(scroll View, y f64) { sender(voidptr(scroll), voidptr(macos.sel('setContentOffset:animated:')), offset, false) } -fn new_native_view(frame Rect, bg_hex u32, radius f64) View { +fn new_native_view(frame Rect, box BoxStyle) View { view := macos.msg_id_rect(macos.alloc('UIView'), 'initWithFrame:', native_rect(frame)) - set_background(view, bg_hex) - set_corner_radius(view, radius) + set_box_background(view, box) + set_corner_radius(view, box.radius) return view } -fn new_scroll_view(frame Rect, bg_hex u32, radius f64) View { +fn new_scroll_view(frame Rect, box BoxStyle) View { scroll := macos.msg_id_rect(macos.alloc('UIScrollView'), 'initWithFrame:', native_rect(frame)) - set_background(scroll, bg_hex) - set_corner_radius(scroll, radius) + set_box_background(scroll, box) + set_corner_radius(scroll, box.radius) macos.msg_void_bool(scroll, 'setAlwaysBounceVertical:', true) macos.msg_void_i64(scroll, 'setKeyboardDismissMode:', 1) macos.msg_void_i64(scroll, 'setContentInsetAdjustmentBehavior:', 2) @@ -538,7 +543,7 @@ fn new_text_area_view(el Element) View { fn update_text_area_view(view View, el Element, declared_text_changed bool) { macos.msg_void_rect(view, 'setFrame:', native_rect(el.frame)) - set_background(view, el.box.bg) + set_box_background(view, el.box) macos.msg_void1(view, 'setTextColor:', ios.color(el.text_style.color)) macos.msg_void1(view, 'setFont:', font(el.text_style.size, el.text_style.bold)) macos.msg_void_bool(view, 'setEditable:', !el.readonly && el.enabled) @@ -551,7 +556,7 @@ fn update_text_area_view(view View, el Element, declared_text_changed bool) { } fn new_dropdown_view(el Element) View { - view := new_button_view(el.frame, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, el.box.radius, el.text_style.lines) + view := new_button_view(el.frame, el.text, el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.lines) update_dropdown_view(view, el, true) return view } @@ -561,28 +566,28 @@ fn update_dropdown_view(view View, el Element, declared_text_changed bool) { if declared_text_changed && macos.utf8_string(macos.msg_id(view, 'currentTitle')) != el.text { macos.msg_void2(view, 'setTitle:forState:', macos.nsstring(el.text), macos.Id(usize(0))) } - set_background(view, el.box.bg) + set_box_background(view, el.box) set_corner_radius(view, el.box.radius) native_configure_dropdown(view, el.menu) } -fn new_button_view(frame Rect, title string, bg_hex u32, text_hex u32, size f64, bold bool, radius f64, lines int) View { +fn new_button_view(frame Rect, title string, box BoxStyle, text_hex u32, size f64, bold bool, lines int) View { btn := macos.msg_id_u64(macos.get_class('UIButton'), 'buttonWithType:', u64(0)) // buttonWithType: is autoreleased. Retain so all native_create_element // results follow the same +1 ownership contract. macos.msg_id(btn, 'retain') - update_button_view(btn, frame, title, bg_hex, text_hex, size, bold, radius, lines) + update_button_view(btn, frame, title, box, text_hex, size, bold, lines) return btn } -fn update_button_view(btn View, frame Rect, title string, bg_hex u32, text_hex u32, size f64, bold bool, radius f64, lines int) { +fn update_button_view(btn View, frame Rect, title string, box BoxStyle, text_hex u32, size f64, bold bool, lines int) { macos.msg_void_rect(btn, 'setFrame:', native_rect(frame)) macos.msg_void2(btn, 'setTitle:forState:', macos.nsstring(title), macos.Id(usize(0))) macos.msg_void2(btn, 'setTitleColor:forState:', ios.color(text_hex), macos.Id(usize(0))) - set_background(btn, bg_hex) + set_box_background(btn, box) title_label := macos.msg_id(btn, 'titleLabel') macos.msg_void1(title_label, 'setFont:', font(size, bold)) - set_corner_radius(btn, radius) + set_corner_radius(btn, box.radius) macos.msg_void_i64(title_label, 'setNumberOfLines:', i64(lines)) macos.msg_void_i64(title_label, 'setTextAlignment:', 1) macos.msg_void_i64(title_label, 'setLineBreakMode:', 4) @@ -594,14 +599,13 @@ fn checkbox_title(el Element) string { } fn new_checkbox_view(el Element) View { - view := new_button_view(el.frame, checkbox_title(el), el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, 0, el.text_style.lines) + view := new_button_view(el.frame, checkbox_title(el), el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.lines) update_checkbox_view(view, el) return view } fn update_checkbox_view(view View, el Element) { - update_button_view(view, el.frame, checkbox_title(el), el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, 0, el.text_style.lines) - macos.msg_void1(view, 'setBackgroundColor:', macos.msg_id(macos.get_class('UIColor'), 'clearColor')) + update_button_view(view, el.frame, checkbox_title(el), el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.lines) macos.msg_void_i64(view, 'setContentHorizontalAlignment:', 1) } @@ -620,7 +624,7 @@ fn update_switch_control_view(view View, el Element) { } fn new_toggle_button_view(el Element) View { - view := new_button_view(el.frame, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, el.box.radius, el.text_style.lines) + view := new_button_view(el.frame, el.text, el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.lines) update_toggle_button_view(view, el) return view } @@ -628,7 +632,7 @@ fn new_toggle_button_view(el Element) View { fn update_toggle_button_view(view View, 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 } - update_button_view(view, el.frame, el.text, box.bg, style.color, style.size, style.bold, box.radius, style.lines) + update_button_view(view, el.frame, el.text, box, style.color, style.size, style.bold, style.lines) macos.msg_void_bool(view, 'setSelected:', el.checked) } @@ -689,15 +693,15 @@ fn native_snap_slider_value(view View, spec SliderSpec) f64 { return value } -fn new_text_field_view(frame Rect, placeholder string, t string, bg_hex u32, text_hex u32, size f64, radius f64, keyboard int, secure bool) View { +fn new_text_field_view(frame Rect, placeholder string, t string, box BoxStyle, text_hex u32, size f64, keyboard int, secure bool) View { field := macos.msg_id_rect(macos.alloc('UITextField'), 'initWithFrame:', native_rect(frame)) - update_text_field_view(field, frame, placeholder, t, bg_hex, text_hex, size, radius, keyboard, secure, true, true, 12, false, true) + update_text_field_view(field, frame, placeholder, t, box, text_hex, size, keyboard, secure, true, true, 12, false, true) return field } -fn update_text_field_view(field View, frame Rect, placeholder string, t string, bg_hex u32, text_hex u32, size f64, radius f64, keyboard int, secure bool, autocorrect bool, declared_text_changed bool, padding_left f64, readonly bool, enabled bool) { +fn update_text_field_view(field View, frame Rect, placeholder string, t string, box BoxStyle, text_hex u32, size f64, keyboard int, secure bool, autocorrect bool, declared_text_changed bool, padding_left f64, readonly bool, enabled bool) { macos.msg_void_rect(field, 'setFrame:', native_rect(frame)) - set_background(field, bg_hex) + set_box_background(field, box) macos.msg_void1(field, 'setTextColor:', ios.color(text_hex)) macos.msg_void1(field, 'setFont:', font(size, false)) macos.msg_void1(field, 'setPlaceholder:', macos.nsstring(placeholder)) @@ -711,7 +715,7 @@ fn update_text_field_view(field View, frame Rect, placeholder string, t string, macos.msg_void_i64(field, 'setAutocorrectionType:', if autocorrect { i64(0) } else { i64(1) }) macos.msg_void_bool(field, 'setEnabled:', enabled && !readonly) macos.msg_void_i64(field, 'setClearButtonMode:', 1) - set_corner_radius(field, radius) + set_corner_radius(field, box.radius) mut pad := macos.msg_id(field, 'leftView') if pad == unsafe { nil } { pad = macos.msg_id_rect(macos.alloc('UIView'), 'initWithFrame:', macos.rect(0, 0, padding_left, frame.height)) @@ -821,7 +825,7 @@ fn render_root(declared Element) { g_toggle_ids = map[u64]string{} g_toggle_views = map[u64]View{} g_scroll_ids = map[string]bool{} - set_background(g_root_view, root.box.bg) + set_box_background(g_root_view, root.box) mut active := map[string]bool{} render_children(g_root_view, root.children, '', mut active) remove_stale_nodes(active) @@ -869,22 +873,22 @@ fn render_children(parent View, children []Element, parent_key string, mut activ fn native_create_element(el Element) View { return match el.kind { .screen { View(unsafe { nil }) } - .view { new_native_view(el.frame, el.box.bg, el.box.radius) } - .scroll { new_scroll_view(el.frame, el.box.bg, el.box.radius) } + .view { new_native_view(el.frame, el.box) } + .scroll { new_scroll_view(el.frame, el.box) } .label { new_label_view(el.frame, el.text, el.text_style.color, el.text_style.size, el.text_style.bold, align_value(el.text_style.align), el.text_style.lines) } .image { new_image_view(el.frame, el.image_path, el.rotation) } .button { - new_button_view(el.frame, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, el.box.radius, el.text_style.lines) + new_button_view(el.frame, el.text, el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.lines) } .checkbox { new_checkbox_view(el) } .switch_control { new_switch_control_view(el) } .toggle_button { new_toggle_button_view(el) } .dropdown { new_dropdown_view(el) } .text_field { - field := new_text_field_view(el.frame, el.placeholder, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.box.radius, el.keyboard, el.secure) - update_text_field_view(field, el.frame, el.placeholder, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.box.radius, el.keyboard, el.secure, el.autocorrect, true, el.padding_left, el.readonly, el.enabled) + field := new_text_field_view(el.frame, el.placeholder, el.text, el.box, el.text_style.color, el.text_style.size, el.keyboard, el.secure) + update_text_field_view(field, el.frame, el.placeholder, el.text, el.box, el.text_style.color, el.text_style.size, el.keyboard, el.secure, el.autocorrect, true, el.padding_left, el.readonly, el.enabled) field } .text_area { new_text_area_view(el) } @@ -897,12 +901,12 @@ fn native_update_element(native View, el Element, declared_text_changed bool) { .screen {} .view { macos.msg_void_rect(native, 'setFrame:', native_rect(el.frame)) - set_background(native, el.box.bg) + set_box_background(native, el.box) set_corner_radius(native, el.box.radius) } .scroll { macos.msg_void_rect(native, 'setFrame:', native_rect(el.frame)) - set_background(native, el.box.bg) + set_box_background(native, el.box) set_corner_radius(native, el.box.radius) } .label { @@ -910,14 +914,14 @@ fn native_update_element(native View, el Element, declared_text_changed bool) { } .image { update_image_view(native, el.frame, el.image_path, el.rotation) } .button { - update_button_view(native, el.frame, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.text_style.bold, el.box.radius, el.text_style.lines) + update_button_view(native, el.frame, el.text, el.box, el.text_style.color, el.text_style.size, el.text_style.bold, el.text_style.lines) } .checkbox { update_checkbox_view(native, el) } .switch_control { update_switch_control_view(native, el) } .toggle_button { update_toggle_button_view(native, el) } .dropdown { update_dropdown_view(native, el, declared_text_changed) } .text_field { - update_text_field_view(native, el.frame, el.placeholder, el.text, el.box.bg, el.text_style.color, el.text_style.size, el.box.radius, el.keyboard, el.secure, el.autocorrect, declared_text_changed, el.padding_left, el.readonly, el.enabled) + update_text_field_view(native, el.frame, el.placeholder, el.text, el.box, el.text_style.color, el.text_style.size, el.keyboard, el.secure, el.autocorrect, declared_text_changed, el.padding_left, el.readonly, el.enabled) } .text_area { update_text_area_view(native, el, declared_text_changed) } .slider { update_slider_view(native, el) } @@ -1045,7 +1049,7 @@ fn register_native_handlers(native View, el Element) { fn render_element(parent View, el Element, key string, mut active map[string]bool) View { active[key] = true if el.kind == .screen { - set_background(parent, el.box.bg) + set_box_background(parent, el.box) render_children(parent, el.children, key, mut active) return parent } diff --git a/windows/native_helpers_windows.h b/windows/native_helpers_windows.h index d53196c..38dc61f 100644 --- a/windows/native_helpers_windows.h +++ b/windows/native_helpers_windows.h @@ -29,6 +29,7 @@ #define UI2_WM_REFRESH (WM_APP + 77) #define UI2_WM_TRAY (WM_APP + 78) +#define UI2_WM_PAINT_BACKGROUND (WM_APP + 79) enum { UI2_WIN_VIEW = 1, @@ -53,6 +54,8 @@ extern int ui2_windows_context_menu(void *hwnd, int screen_x, int screen_y); extern int ui2_windows_cursor(void *hwnd); extern void ui2_windows_control_pointer(void *hwnd, unsigned int message, int x, int y); extern void ui2_windows_control_border(void *hwnd); +extern int ui2_windows_is_transparent_button(void *hwnd); +extern int ui2_windows_paint_transparent_button(void *hwnd); static inline void ui2_win_refresh_text_font(HWND hwnd); @@ -142,7 +145,11 @@ static LRESULT CALLBACK ui2_win_control_subclass(HWND hwnd, UINT message, WPARAM ui2_windows_control_pointer(hwnd, message, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); if (!IsWindow(hwnd)) return 0; } + if (message == WM_ERASEBKGND && ui2_windows_is_transparent_button(hwnd)) { + return 1; + } if (message == WM_PAINT) { + if (ui2_windows_paint_transparent_button(hwnd)) return 0; LRESULT result = DefSubclassProc(hwnd, message, wparam, lparam); ui2_win_draw_placeholder(hwnd); ui2_windows_control_border(hwnd); @@ -1010,6 +1017,79 @@ static inline void ui2_win_paint_control_border(void *hwnd_ptr, unsigned int col ReleaseDC(hwnd, dc); } +static inline void ui2_win_fill_background(HDC dc, const RECT *rect, + unsigned int background, double radius) { + HBRUSH brush = CreateSolidBrush(ui2_win_color(background)); + if (brush == NULL) return; + if (radius > 0.5) { + HPEN pen = CreatePen(PS_NULL, 0, ui2_win_color(background)); + HGDIOBJ old_brush = SelectObject(dc, brush); + HGDIOBJ old_pen = SelectObject(dc, pen); + int diameter = (int)(radius * 2.0 + 0.5); + RoundRect(dc, rect->left, rect->top, rect->right, rect->bottom, diameter, diameter); + SelectObject(dc, old_pen); + SelectObject(dc, old_brush); + DeleteObject(pen); + } else { + FillRect(dc, rect, brush); + } + DeleteObject(brush); +} + +static inline void ui2_win_paint_parent_background(HWND hwnd, HDC dc) { + HWND parent = GetParent(hwnd); + if (parent == NULL) return; + POINT parent_origin = {0, 0}; + MapWindowPoints(parent, hwnd, &parent_origin, 1); + int saved = SaveDC(dc); + OffsetViewportOrgEx(dc, parent_origin.x, parent_origin.y, NULL); + SendMessageW(parent, UI2_WM_PAINT_BACKGROUND, (WPARAM)dc, 0); + if (saved != 0) { + RestoreDC(dc, saved); + } else { + OffsetViewportOrgEx(dc, -parent_origin.x, -parent_origin.y, NULL); + } +} + +static inline void ui2_win_paint_background_into(void *hwnd_ptr, void *dc_ptr, + unsigned int background, double radius, int transparent, + unsigned int border_color, double border_left, double border_top, + double border_right, double border_bottom) { + HWND hwnd = (HWND)hwnd_ptr; + HDC dc = (HDC)dc_ptr; + if (hwnd == NULL || dc == NULL) return; + + // UI2Container parents paint with WS_CLIPCHILDREN, so a child cannot leave + // clear pixels untouched. Rebuild the ancestor backdrop in this DC first; + // this preserves both color-key pixels and rounded ancestor boundaries. + HWND parent = GetParent(hwnd); + ui2_win_paint_parent_background(hwnd, dc); + + RECT rect; + GetClientRect(hwnd, &rect); + if (parent == NULL && transparent) { + // A top-level window has no native surface to inherit from. Its declared + // background is the deterministic clear color (and may be a color key). + ui2_win_fill_background(dc, &rect, background, 0.0); + } + if (!transparent) { + ui2_win_fill_background(dc, &rect, background, radius); + } + HRGN clip = NULL; + if (radius > 0.5) { + int diameter = (int)(radius * 2.0 + 0.5); + clip = CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1, + diameter, diameter); + if (clip != NULL) SelectClipRgn(dc, clip); + } + ui2_win_draw_borders(dc, &rect, border_color, border_left, border_top, + border_right, border_bottom); + if (clip != NULL) { + SelectClipRgn(dc, NULL); + DeleteObject(clip); + } +} + static inline void ui2_win_paint_background(void *hwnd_ptr, unsigned int background, double radius, int transparent, unsigned int border_color, double border_left, double border_top, double border_right, double border_bottom) { @@ -1017,29 +1097,29 @@ static inline void ui2_win_paint_background(void *hwnd_ptr, unsigned int backgro PAINTSTRUCT paint; HDC dc = BeginPaint(hwnd, &paint); if (dc != NULL) { + ui2_win_paint_background_into(hwnd, dc, background, radius, transparent, + border_color, border_left, border_top, border_right, border_bottom); + } + EndPaint(hwnd, &paint); +} + +static inline void ui2_win_paint_transparent_button(void *hwnd_ptr, + unsigned int foreground, double radius, unsigned int border_color, + double border_left, double border_top, double border_right, + double border_bottom) { + HWND hwnd = (HWND)hwnd_ptr; + if (hwnd == NULL) return; + PAINTSTRUCT paint; + HDC dc = BeginPaint(hwnd, &paint); + if (dc != NULL) { + ui2_win_paint_parent_background(hwnd, dc); RECT rect; GetClientRect(hwnd, &rect); - if (!transparent) { - HBRUSH brush = CreateSolidBrush(ui2_win_color(background)); - if (radius > 0.5) { - HPEN pen = CreatePen(PS_NULL, 0, ui2_win_color(background)); - HGDIOBJ old_brush = SelectObject(dc, brush); - HGDIOBJ old_pen = SelectObject(dc, pen); - int diameter = (int)(radius * 2.0 + 0.5); - RoundRect(dc, rect.left, rect.top, rect.right, rect.bottom, diameter, diameter); - SelectObject(dc, old_pen); - SelectObject(dc, old_brush); - DeleteObject(pen); - } else { - FillRect(dc, &rect, brush); - } - DeleteObject(brush); - } HRGN clip = NULL; if (radius > 0.5) { int diameter = (int)(radius * 2.0 + 0.5); - clip = CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1, - diameter, diameter); + clip = CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, + rect.bottom + 1, diameter, diameter); if (clip != NULL) SelectClipRgn(dc, clip); } ui2_win_draw_borders(dc, &rect, border_color, border_left, border_top, @@ -1048,6 +1128,33 @@ static inline void ui2_win_paint_background(void *hwnd_ptr, unsigned int backgro SelectClipRgn(dc, NULL); DeleteObject(clip); } + + int length = GetWindowTextLengthW(hwnd); + wchar_t *text = (wchar_t *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + (size_t)(length + 1) * sizeof(wchar_t)); + if (text != NULL) { + GetWindowTextW(hwnd, text, length + 1); + HFONT font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0); + HGDIOBJ old_font = font == NULL ? NULL : SelectObject(dc, font); + int old_mode = SetBkMode(dc, TRANSPARENT); + COLORREF old_color = SetTextColor(dc, IsWindowEnabled(hwnd) + ? ui2_win_color(foreground) : GetSysColor(COLOR_GRAYTEXT)); + RECT text_rect = rect; + if ((SendMessageW(hwnd, BM_GETSTATE, 0, 0) & BST_PUSHED) != 0) { + OffsetRect(&text_rect, 1, 1); + } + DrawTextW(dc, text, length, &text_rect, DT_CENTER | DT_VCENTER + | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX); + SetTextColor(dc, old_color); + SetBkMode(dc, old_mode); + if (old_font != NULL) SelectObject(dc, old_font); + HeapFree(GetProcessHeap(), 0, text); + } + if (GetFocus() == hwnd) { + RECT focus = rect; + InflateRect(&focus, -3, -3); + DrawFocusRect(dc, &focus); + } } EndPaint(hwnd, &paint); } diff --git a/windows/ui_windows.v b/windows/ui_windows.v index 9b3c0bc..57df163 100644 --- a/windows/ui_windows.v +++ b/windows/ui_windows.v @@ -118,6 +118,10 @@ fn C.ui2_win_apply_control_colors(dc voidptr, foreground u32, background u32, tr fn C.ui2_win_paint_background(hwnd voidptr, background u32, radius f64, transparent int, border_color u32, border_left f64, border_top f64, border_right f64, border_bottom f64) +fn C.ui2_win_paint_background_into(hwnd voidptr, dc voidptr, background u32, radius f64, transparent int, border_color u32, border_left f64, border_top f64, border_right f64, border_bottom f64) + +fn C.ui2_win_paint_transparent_button(hwnd voidptr, foreground u32, radius f64, border_color u32, border_left f64, border_top f64, border_right f64, border_bottom f64) + fn C.ui2_win_paint_control_border(hwnd voidptr, color u32, radius f64, left f64, top f64, right f64, bottom f64) fn C.ui2_win_invalidate(hwnd voidptr) @@ -183,6 +187,7 @@ const win_wm_lbutton_up = u32(0x0202) const win_wm_mouse_wheel = u32(0x020a) const win_wm_dropfiles = u32(0x0233) const win_wm_refresh = u32(0x8000 + 77) +const win_wm_paint_background = u32(0x8000 + 79) const win_bn_clicked = 0 const win_cbn_selchange = 1 @@ -308,6 +313,10 @@ fn windows_bool(value bool) int { return if value { 1 } else { 0 } } +fn windows_uses_transparent_button_paint(kind Kind, box BoxStyle) bool { + return box.transparent && kind in [.button, .toggle_button] +} + fn windows_align(align Align) int { return match align { .left { 0 } @@ -1367,6 +1376,18 @@ fn ui2_windows_window_proc(hwnd voidptr, message u32, wparam usize, lparam isize .checkbox, ]), brush) } + win_wm_paint_background { + box := if hwnd == st.root { + st.node_boxes[''] or { BoxStyle{} } + } else { + key := st.handle_keys[windows_handle_id(hwnd)] or { return 0 } + st.node_boxes[key] or { return 0 } + } + C.ui2_win_paint_background_into(hwnd, voidptr(wparam), box.bg, box.radius, + windows_bool(box.transparent), box.border_color, box.border_left, + box.border_top, box.border_right, box.border_bottom) + return 0 + } win_wm_paint { if hwnd == st.root { box := st.node_boxes[''] or { BoxStyle{} } @@ -1468,6 +1489,30 @@ fn ui2_windows_control_border(hwnd voidptr) { box.border_top, box.border_right, box.border_bottom) } +@[export: 'ui2_windows_paint_transparent_button'] +fn ui2_windows_paint_transparent_button(hwnd voidptr) int { + st := windows_state() + key := st.handle_keys[windows_handle_id(hwnd)] or { return 0 } + kind := st.node_kinds[key] or { return 0 } + box := st.node_boxes[key] or { return 0 } + if !windows_uses_transparent_button_paint(kind, box) { + return 0 + } + style := st.node_text_styles[key] or { TextStyle{} } + C.ui2_win_paint_transparent_button(hwnd, style.color, box.radius, box.border_color, + box.border_left, box.border_top, box.border_right, box.border_bottom) + return 1 +} + +@[export: 'ui2_windows_is_transparent_button'] +fn ui2_windows_is_transparent_button(hwnd voidptr) int { + st := windows_state() + key := st.handle_keys[windows_handle_id(hwnd)] or { return 0 } + kind := st.node_kinds[key] or { return 0 } + box := st.node_boxes[key] or { return 0 } + return windows_bool(windows_uses_transparent_button_paint(kind, box)) +} + @[export: 'ui2_windows_edit_submit'] fn ui2_windows_edit_submit(hwnd voidptr) int { st := windows_state() diff --git a/windows/ui_windows_test.v b/windows/ui_windows_test.v index 9e73de7..5dd7c41 100644 --- a/windows/ui_windows_test.v +++ b/windows/ui_windows_test.v @@ -69,6 +69,16 @@ $if !ui2_custom_rendering ? { assert windows_content_height(children) == 115 } + fn test_windows_transparent_push_buttons_use_custom_painting() { + transparent := BoxStyle{ + transparent: true + } + assert windows_uses_transparent_button_paint(.button, transparent) + assert windows_uses_transparent_button_paint(.toggle_button, transparent) + assert !windows_uses_transparent_button_paint(.button, BoxStyle{}) + assert !windows_uses_transparent_button_paint(.checkbox, transparent) + } + fn windows_test_font_family(font voidptr) string { mut buffer := []u16{len: 32} C.ui2_win_font_family(font, unsafe { &buffer[0] }, buffer.len)