diff --git a/src/Uno.UI/UI/Xaml/UIElement.wasm.cs b/src/Uno.UI/UI/Xaml/UIElement.wasm.cs index b70f3d302c6e..13ebabcad737 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.wasm.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.wasm.cs @@ -415,17 +415,7 @@ protected virtual void OnVisibilityChanged(Visibility oldValue, Visibility newVi InvalidateMeasure(); UpdateHitTest(); - if (newVisibility == Visibility.Visible) - { - ResetStyle("visibility"); - } - else - { - // Note: On wasm when we have an 'hidden' (or 'collapsed') element, its height is used to compute the native 'scrollHeight', - // driving the SV to flicker when we scroll while at the bottom of the viewport (if those hidden element would have increase the viewport if visible). - // To avoid that, we move the element way out of the visible bounds of the view. - SetStyle(("visibility", "hidden"), ("top", "-100000px"), ("left", "-100000px")); - } + WindowManagerInterop.SetVisibility(HtmlId, newVisibility == Visibility.Visible); if (FeatureConfiguration.UIElement.AssignDOMXamlProperties) { diff --git a/src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs b/src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs index 6b7f06e98538..6ca1eb97d784 100644 --- a/src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs +++ b/src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs @@ -779,6 +779,37 @@ private struct WindowManagerSetXUidParams } #endregion + #region SetVisibility + + internal static void SetVisibility(IntPtr htmlId, bool visible) + { + if (UseJavascriptEval) + { + var command = $"Uno.UI.WindowManager.current.setVisibility(\"{htmlId}\", {visible});"; + WebAssemblyRuntime.InvokeJS(command); + } + else + { + var parms = new WindowManagerSetVisibilityParams() + { + HtmlId = htmlId, + Visible = visible, + }; + + TSInteropMarshaller.InvokeJS("Uno:setVisibilityNative", parms); + } + } + + [TSInteropMessage] + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct WindowManagerSetVisibilityParams + { + public IntPtr HtmlId; + + public bool Visible; + } + #endregion + #region SetProperty internal static void SetProperty(IntPtr htmlId, (string name, string value)[] properties) diff --git a/src/Uno.UI/WasmCSS/Uno.UI.css b/src/Uno.UI/WasmCSS/Uno.UI.css index 68d954969db1..574d40d56f23 100644 --- a/src/Uno.UI/WasmCSS/Uno.UI.css +++ b/src/Uno.UI/WasmCSS/Uno.UI.css @@ -63,6 +63,17 @@ svg.uno-uielement { position: fixed; } +.uno-uielement.uno-visibility-collapsed { + /* + Note: On wasm when we have an 'hidden' (or 'collapsed') element, its height is used to compute the native 'scrollHeight', + driving the SV to flicker when we scroll while at the bottom of the viewport (if those hidden element would have increase the viewport if visible). + To avoid that, we move the element way out of the visible bounds of the view. + */ + visibility: hidden !important; + top: -100000px !important; + left: -100000px !important; +} + svg.uno-frameworkelement.uno-unarranged, iframe.uno-frameworkelement.uno-unarranged, img.uno-frameworkelement.uno-unarranged, diff --git a/src/Uno.UI/WasmScripts/Uno.UI.d.ts b/src/Uno.UI/WasmScripts/Uno.UI.d.ts index 7b6e0c56eba4..a539014ac8d0 100644 --- a/src/Uno.UI/WasmScripts/Uno.UI.d.ts +++ b/src/Uno.UI/WasmScripts/Uno.UI.d.ts @@ -141,6 +141,7 @@ declare namespace Uno.UI { static get isLoadEventsEnabled(): boolean; private static readonly unoRootClassName; private static readonly unoUnarrangedClassName; + private static readonly unoCollapsedClassName; private static _cctor; /** * Initialize the WindowManager @@ -227,6 +228,12 @@ declare namespace Uno.UI { */ setXUidNative(pParam: number): boolean; private setXUidInternal; + /** + * Sets the visibility of the specified element + */ + setVisibility(elementId: number, visible: boolean): string; + setVisibilityNative(pParam: number): boolean; + private setVisibilityInternal; /** * Set an attribute for an element. */ @@ -1386,6 +1393,11 @@ declare class WindowManagerSetUnsetClassesParams { CssClassesToUnset: Array; static unmarshal(pData: number): WindowManagerSetUnsetClassesParams; } +declare class WindowManagerSetVisibilityParams { + HtmlId: number; + Visible: boolean; + static unmarshal(pData: number): WindowManagerSetVisibilityParams; +} declare class WindowManagerSetXUidParams { HtmlId: number; Uid: string; diff --git a/src/Uno.UI/WasmScripts/Uno.UI.js b/src/Uno.UI/WasmScripts/Uno.UI.js index 298164977f66..f68efc4a98dd 100644 --- a/src/Uno.UI/WasmScripts/Uno.UI.js +++ b/src/Uno.UI/WasmScripts/Uno.UI.js @@ -576,6 +576,27 @@ var Uno; setXUidInternal(elementId, name) { this.getView(elementId).setAttribute("xuid", name); } + /** + * Sets the visibility of the specified element + */ + setVisibility(elementId, visible) { + this.setVisibilityInternal(elementId, visible); + return "ok"; + } + setVisibilityNative(pParam) { + const params = WindowManagerSetVisibilityParams.unmarshal(pParam); + this.setVisibilityInternal(params.HtmlId, params.Visible); + return true; + } + setVisibilityInternal(elementId, visible) { + const element = this.getView(elementId); + if (visible) { + element.classList.remove(WindowManager.unoCollapsedClassName); + } + else { + element.classList.add(WindowManager.unoCollapsedClassName); + } + } /** * Set an attribute for an element. */ @@ -1819,6 +1840,7 @@ var Uno; WindowManager._isLoadEventsEnabled = false; WindowManager.unoRootClassName = "uno-root-element"; WindowManager.unoUnarrangedClassName = "uno-unarranged"; + WindowManager.unoCollapsedClassName = "uno-visibility-collapsed"; WindowManager._cctor = (() => { WindowManager.initMethods(); UI.HtmlDom.initPolyfills(); @@ -5001,6 +5023,19 @@ class WindowManagerSetUnsetClassesParams { } } /* TSBindingsGenerator Generated code -- this code is regenerated on each build */ +class WindowManagerSetVisibilityParams { + static unmarshal(pData) { + const ret = new WindowManagerSetVisibilityParams(); + { + ret.HtmlId = Number(Module.getValue(pData + 0, "*")); + } + { + ret.Visible = Boolean(Module.getValue(pData + 4, "i32")); + } + return ret; + } +} +/* TSBindingsGenerator Generated code -- this code is regenerated on each build */ class WindowManagerSetXUidParams { static unmarshal(pData) { const ret = new WindowManagerSetXUidParams(); diff --git a/src/Uno.UI/ts/WindowManager.ts b/src/Uno.UI/ts/WindowManager.ts index d7cbf2fe3d76..ac771d32a36b 100644 --- a/src/Uno.UI/ts/WindowManager.ts +++ b/src/Uno.UI/ts/WindowManager.ts @@ -28,6 +28,7 @@ namespace Uno.UI { private static readonly unoRootClassName = "uno-root-element"; private static readonly unoUnarrangedClassName = "uno-unarranged"; + private static readonly unoCollapsedClassName = "uno-visibility-collapsed"; private static _cctor = (() => { WindowManager.initMethods(); @@ -364,6 +365,31 @@ namespace Uno.UI { this.getView(elementId).setAttribute("xuid", name); } + /** + * Sets the visibility of the specified element + */ + public setVisibility(elementId: number, visible: boolean): string { + this.setVisibilityInternal(elementId, visible); + return "ok"; + } + + public setVisibilityNative(pParam: number): boolean { + const params = WindowManagerSetVisibilityParams.unmarshal(pParam); + this.setVisibilityInternal(params.HtmlId, params.Visible); + return true; + } + + private setVisibilityInternal(elementId: number, visible: boolean): void { + const element = this.getView(elementId); + + if (visible) { + element.classList.remove(WindowManager.unoCollapsedClassName); + } + else { + element.classList.add(WindowManager.unoCollapsedClassName); + } + } + /** * Set an attribute for an element. */ diff --git a/src/Uno.UI/tsBindings/WindowManagerSetVisibilityParams.ts b/src/Uno.UI/tsBindings/WindowManagerSetVisibilityParams.ts new file mode 100644 index 000000000000..da78564512b2 --- /dev/null +++ b/src/Uno.UI/tsBindings/WindowManagerSetVisibilityParams.ts @@ -0,0 +1,20 @@ +/* TSBindingsGenerator Generated code -- this code is regenerated on each build */ +class WindowManagerSetVisibilityParams +{ + /* Pack=4 */ + public HtmlId : number; + public Visible : boolean; + public static unmarshal(pData:number) : WindowManagerSetVisibilityParams + { + const ret = new WindowManagerSetVisibilityParams(); + + { + ret.HtmlId = Number(Module.getValue(pData + 0, "*")); + } + + { + ret.Visible = Boolean(Module.getValue(pData + 4, "i32")); + } + return ret; + } +}