Repository: tauri-apps/wry (language: rust)

Issue:
Windows: Support setting `ICoreWebView2EnvironmentOptions8::ScrollBarStyle`
# Is your feature request related to a problem? Please describe.
I need a way to set the WebView2 instance on Windows to use Fluent-style overlay scrollbars in Tauri. A member of the project has informed me on Discord that Wry exposes a way to set some platform-specific attributes by use of `PlatformSpecificWebViewAttributes` that are then used to configure settings in the WebView2 options and environment options.

# Describe the solution you'd like
I propose adding a new field to `PlatformSpecificWebViewAttributes` on Windows, and a setter method to `WebViewBuilderExtWindows` to update this field. I'm unsure whether to use a boolean where `true` = fluent overlay scrollbars and `false` = default scrollbars, or an enum that exposes both `Default` and `FluentOverlay`. I feel the latter would be better for extensibility if Microsoft extend the list of scrollbar styles in the future.

I expect the API would look something like this (documentation omitted for brevity):

```rust
#[cfg(windows)]
#[derive(Clone, Copy, Default)]
pub enum ScrollbarStyle {
  #[default]
  Default,
  FluentOverlay,
}

...

#[cfg(windows)]
#[derive(Clone)]
pub(crate) struct PlatformSpecificWebViewAttributes {
  ...
  scrollbar_style: ScrollbarStyle,
}

#[cfg(windows)]
impl Default for PlatformSpecificWebViewAttributes {
  fn default() -> Self {
    Self {
      ...
      scrollbar_style: ScrollbarStyle::Default,
    }
  }
}

#[cfg(windows)]
pub trait WebViewBuilderExtWindows {
  ...
  fn with_scrollbar_style(self, style: ScrollbarStyle) -> Self;
}

#[cfg(windows)]
impl WebViewBuilderExtWindows for WebViewBuilder<'_> {
  ...
  fn with_scrollbar_style(self, style: ScrollbarStyle) -> Self {
    self.platform_specific.scrollbar_style = style;
    self
  }
}
```

# Describe alternatives you've considered
I **cannot** use a browser flag for this, as per [the Microsoft Documentation on WebView2 browser flags](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/webview-features-flags) where it says that browser feature flags are subject to being removed or altered at any time.

Also, it was either this or adding and exposing a Windows-specific wrapper around all of `ICoreWebView2EnvironmentOptions*`. I feel that for now, this will do.



Discussion / hints:
> I'm unsure whether to use a boolean where `true` = fluent overlay scrollbars and `false` = default scrollbars, or an enum that exposes both `Default` and `FluentOverlay`. I feel the latter would be better for extensibility if Microsoft extend the list of scrollbar styles in the future.

An enum sounds fine

Do you plan to work on this in a PR? I can assign the issue to you in that case.
I'm fine with adding this myself.
Ah. I think I've encountered a bit of a problem: I can't figure out how I am going to expose this in Tauri, given that this is Windows-specific functionality with an enum that only exists in Wry when compiling for Windows.
one step at a time, you only need to worry about exposing it as a method on `WebviewBuilderExtWindows`, then later we can open a PR in Tauri, that will call this method. 
I have it exposed on `WebviewBuilderExtWindows` in my `wry` fork already, on the `feat/windows-add-scrollbar-style-option` branch. The reason I'm trying to figure out how to expose it in Tauri right now is so that I can test it, and make sure it's actually doing what I want it to.
you can see this commit for inspiration https://github.com/tauri-apps/tauri/commit/3dc38b150ea8c59c8ba67fd586f921016928f47c
...I just realised I don't need to expose it in Tauri to test it just yet. I can just do something dirty - force it inside a local patch of `tauri_runtime_wry`, then revert that when I'm done.
...

Well that's... **very** annoying.

Turns out that despite them being exposed, and despite [the Microsoft documentation for `ICoreWebView2EnvironmentOptions`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2651.64) clearly showing the ability to cast `CoreWebView2EnvironmentOptions` to `ICoreWebView2EnvironmentOptions4` and above, it turns out that `webview2-com` doesn't actually reflect the implementations of `ICoreWebView2EnvironmentOptions4` or above on `CoreWebView2EnvironmentOptions`. So everything lines up according to the C++ documentation, but I'm completely unable to actually *cast* to `ICoreWebView2EnvironmentOptions8` in order to set the scrollbar style.

I'm going to submit a request for the missing COM interfaces to be added.
`webview2-com` does have that https://docs.rs/webview2-com/latest/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2EnvironmentOptions8.html , we just haven't updated the version in wry, probably because we need to update `windows-sys` and `windows` crate across our repos

gonna start working on that today
It has the interface defined. So does the version we are using here. It **does not** appear to have an implementation of that interface reflected on `CoreWebView2EnvironmentOptions`, which means we cannot cast to it.
I see, it is missing in https://github.com/wravery/webview2-rs/blob/2a4d92c91002f91b49fd282abbbe8ec8d3318339/crates/webview2-com/src/options.rs#L26-L31
I've opened a request in `webview2-com`: https://github.com/wravery/webview2-rs/issues/30
@amrbashir Good news: The missing COM interface implementations were added to `CoreWebView2EnvironmentOptions` in `webview2-com@0.33.0`, so this can go forward once Wry updates to the latest version.

Also, it turned out that the raw `ICoreWebView2EnvironmentOptions4` had a problem, so it was replaced with a custom-made `IFixedEnvironmentOptions4` instead.
webview2-com is updated in https://github.com/tauri-apps/wry/pull/1341
Alright. I'll rebase onto that commit once it lands, then open a PR for both this and moving to using the `CoreWebView2EnvironmentOptions` API directly.

Fix patch:
diff --git a/.changes/1344-with_scroll_bar_style.md b/.changes/1344-with_scroll_bar_style.md
new file mode 100644
index 000000000..36845ec6b
--- /dev/null
+++ b/.changes/1344-with_scroll_bar_style.md
@@ -0,0 +1,5 @@
+---
+"wry": minor
+---
+
+Windows: Implement `WebViewBuilderExtWindows::with_scroll_bar_style` to allow opting into Fluent Overlay style scrollbars.
diff --git a/src/lib.rs b/src/lib.rs
index 88eb0a389..114c1f060 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -238,6 +238,8 @@ pub use wkwebview::{PrintMargin, PrintOptions};
 #[cfg(target_os = "windows")]
 pub(crate) mod webview2;
 #[cfg(target_os = "windows")]
+pub use self::webview2::ScrollBarStyle;
+#[cfg(target_os = "windows")]
 use self::webview2::*;
 #[cfg(target_os = "windows")]
 use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
@@ -1088,6 +1090,7 @@ pub(crate) struct PlatformSpecificWebViewAttributes {
   browser_accelerator_keys: bool,
   theme: Option<Theme>,
   use_https: bool,
+  scroll_bar_style: ScrollBarStyle,
 }
 
 #[cfg(windows)]
@@ -1098,6 +1101,7 @@ impl Default for PlatformSpecificWebViewAttributes {
       browser_accelerator_keys: true, // This is WebView2's default behavior
       theme: None,
       use_https: false, // To match macOS & Linux behavior in the context of mixed content.
+      scroll_bar_style: ScrollBarStyle::default(),
     }
   }
 }
@@ -1140,6 +1144,15 @@ pub trait WebViewBuilderExtWindows {
   ///
   /// The default value is `false`.
   fn with_https_scheme(self, enabled: bool) -> Self;
+
+  /// Specifies the native scrollbar style to use with webview2.
+  /// CSS styles that modify the scrollbar are applied on top of the native appearance configured here.
+  ///
+  /// Defaults to [`ScrollbarStyle::Default`] which is the browser default used by Microsoft Edge.
+  ///
+  /// Requires WebView2 Runtime version 125.0.2535.41 or higher, does nothing on older versions,
+  /// see https://learn.microsoft.com/en-us/microsoft-edge/webview2/release-notes/?tabs=dotnetcsharp#10253541
+  fn with_scroll_bar_style(self, style: ScrollBarStyle) -> Self;
 }
 
 #[cfg(windows)]
@@ -1163,6 +1176,11 @@ impl WebViewBuilderExtWindows for WebViewBuilder<'_> {
     self.platform_specific.use_https = enabled;
     self
   }
+
+  fn with_scroll_bar_style(mut self, style: ScrollBarStyle) -> Self {
+    self.platform_specific.scroll_bar_style = style;
+    self
+  }
 }
 
 #[cfg(target_os = "android")]
diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs
index 23bc14fea..ff2fdb463 100644
--- a/src/webview2/mod.rs
+++ b/src/webview2/mod.rs
@@ -287,26 +287,30 @@ impl InnerWebView {
       arguments
     });
 
-    let additional_browser_args = HSTRING::from(additional_browser_args);
-
     let (tx, rx) = mpsc::channel();
     CreateCoreWebView2EnvironmentCompletedHandler::wait_for_async_operation(
       Box::new(move |environmentcreatedhandler| unsafe {
-        let options: ICoreWebView2EnvironmentOptions =
-          CoreWebView2EnvironmentOptions::default().into();
+        let options = CoreWebView2EnvironmentOptions::default();
 
-        let _ = options.SetAdditionalBrowserArguments(&additional_browser_args);
+        options.set_additional_browser_arguments(additional_browser_args);
 
         // Get user's system language
         let lcid = GetUserDefaultUILanguage();
         let mut lang = [0; MAX_LOCALE_NAME as usize];
         LCIDToLocaleName(lcid as u32, Some(&mut lang), LOCALE_ALLOW_NEUTRAL_NAMES);
-        options.SetLanguage(PCWSTR::from_raw(lang.as_ptr()))?;
+        options.set_language(String::from_utf16_lossy(&lang));
+
+        let scroll_bar_style = match pl_attrs.scroll_bar_style {
+          ScrollBarStyle::Default => COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT,
+          ScrollBarStyle::FluentOverlay => COREWEBVIEW2_SCROLLBAR_STYLE_FLUENT_OVERLAY,
+        };
+
+        options.set_scroll_bar_style(scroll_bar_style);
 
         CreateCoreWebView2EnvironmentWithOptions(
           PCWSTR::null(),
           &data_directory.unwrap_or_default(),
-          &options,
+          &ICoreWebView2EnvironmentOptions::from(options),
           &environmentcreatedhandler,
         )
         .map_err(Into::into)
@@ -1371,6 +1375,17 @@ impl InnerWebView {
   }
 }
 
+/// The scrollbar style to use in the webview.
+#[derive(Clone, Copy, Default)]
+pub enum ScrollBarStyle {
+  #[default]
+  /// The browser default scrollbar style.
+  Default,
+
+  /// Fluent UI style overlay scrollbars.
+  FluentOverlay,
+}
+
 #[inline]
 fn load_url_with_headers(
   webview: &ICoreWebView2,


Test patch: