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

Issue:
[feat] On Windows, allow to customize the name of window class used
### Describe the problem

Currently, on Windows, all app windows created by tauri have the same class named "Window Class", it would be neet to allow for customization of the Windows' window class name. 

One use case: some windows custom hotkey managers leverage window class names to select windows...

### Describe the solution you'd like

I would like to add a WindowConfig option called `window_classname`. Default should still be "Window Class" : it would be a non-breaking change :)

### Alternatives considered

No alternative, unless simply dumping those tools and find one using other characteristics of windows...

### Additional context

I have patched it on my side and want to contribute it to the upstream project.
It does depend on a new feature on Tao / winit side. Here are the correponding PRs:
- winit : https://github.com/rust-windowing/winit/pull/2978 
  Status : All 55 checks passed, waiting for merge
- tao : https://github.com/tauri-apps/tao/pull/770
  Status : waiting for merge


Discussion / hints:

Fix patch:
diff --git a/.changes/window-class-name-config-api.md b/.changes/window-class-name-config-api.md
new file mode 100644
index 000000000000..13128871bad5
--- /dev/null
+++ b/.changes/window-class-name-config-api.md
@@ -0,0 +1,5 @@
+---
+"@tauri-apps/api": 'minor:feat'
+---
+
+Added `windowClassname` option, when constructing a `Webview` or `WebviewWindow`, to specify the name of the window class on Windows.
diff --git a/.changes/window-class-name-config.md b/.changes/window-class-name-config.md
new file mode 100644
index 000000000000..66e318c0994d
--- /dev/null
+++ b/.changes/window-class-name-config.md
@@ -0,0 +1,6 @@
+---
+"tauri": 'minor:feat'
+"tauri-utils": 'minor:feat'
+---
+
+Added `app > windows > windowClassname` config option to specify the name of the window class on Windows.
diff --git a/.changes/window-class-name.md b/.changes/window-class-name.md
new file mode 100644
index 000000000000..015602e820c8
--- /dev/null
+++ b/.changes/window-class-name.md
@@ -0,0 +1,7 @@
+---
+"tauri": 'minor:feat'
+"tauri-runtime-wry": 'minor:feat'
+"tauri-runtime": 'minor:feat'
+---
+
+Added `WindowBuilder/WebviewWindowBuilder::window_classname` method to specify the name of the window class on Windows.
diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json
index 820fb35a55f0..9efeba50db44 100644
--- a/crates/tauri-cli/config.schema.json
+++ b/crates/tauri-cli/config.schema.json
@@ -397,6 +397,13 @@
           "default": false,
           "type": "boolean"
         },
+        "windowClassname": {
+          "description": "The name of the window class created on Windows to create the window. **Windows only**.",
+          "type": [
+            "string",
+            "null"
+          ]
+        },
         "theme": {
           "description": "The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.",
           "anyOf": [
diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs
index 838def7790ec..65e77ec95670 100644
--- a/crates/tauri-runtime-wry/src/lib.rs
+++ b/crates/tauri-runtime-wry/src/lib.rs
@@ -763,6 +763,11 @@ impl WindowBuilder for WindowBuilderWrapper {
 
     builder = builder.title("Tauri App");
 
+    #[cfg(windows)]
+    {
+      builder = builder.window_classname("Tauri Window");
+    }
+
     builder
   }
 
@@ -846,6 +851,10 @@ impl WindowBuilder for WindowBuilderWrapper {
       if config.center {
         window = window.center();
       }
+
+      if let Some(window_classname) = &config.window_classname {
+        window = window.window_classname(window_classname);
+      }
     }
 
     window
@@ -1106,6 +1115,16 @@ impl WindowBuilder for WindowBuilderWrapper {
       _ => Theme::Light,
     })
   }
+
+  #[cfg(windows)]
+  fn window_classname<S: Into<String>>(mut self, window_classname: S) -> Self {
+    self.inner = self.inner.with_window_classname(window_classname);
+    self
+  }
+  #[cfg(not(windows))]
+  fn window_classname<S: Into<String>>(self, _window_classname: S) -> Self {
+    self
+  }
 }
 
 #[cfg(any(
diff --git a/crates/tauri-runtime/src/window.rs b/crates/tauri-runtime/src/window.rs
index cea526f5f120..69f8eaf8a5c7 100644
--- a/crates/tauri-runtime/src/window.rs
+++ b/crates/tauri-runtime/src/window.rs
@@ -438,6 +438,10 @@ pub trait WindowBuilder: WindowBuilderBase {
   fn has_icon(&self) -> bool;
 
   fn get_theme(&self) -> Option<Theme>;
+
+  /// Sets custom name for Windows' window class. **Windows only**.
+  #[must_use]
+  fn window_classname<S: Into<String>>(self, window_classname: S) -> Self;
 }
 
 /// A window that has yet to be built.
diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json
index 820fb35a55f0..9efeba50db44 100644
--- a/crates/tauri-schema-generator/schemas/config.schema.json
+++ b/crates/tauri-schema-generator/schemas/config.schema.json
@@ -397,6 +397,13 @@
           "default": false,
           "type": "boolean"
         },
+        "windowClassname": {
+          "description": "The name of the window class created on Windows to create the window. **Windows only**.",
+          "type": [
+            "string",
+            "null"
+          ]
+        },
         "theme": {
           "description": "The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.",
           "anyOf": [
diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs
index 4affc4c87cd2..1ff12495c022 100644
--- a/crates/tauri-utils/src/config.rs
+++ b/crates/tauri-utils/src/config.rs
@@ -1429,6 +1429,8 @@ pub struct WindowConfig {
   /// If `true`, hides the window icon from the taskbar on Windows and Linux.
   #[serde(default, alias = "skip-taskbar")]
   pub skip_taskbar: bool,
+  /// The name of the window class created on Windows to create the window. **Windows only**.
+  pub window_classname: Option<String>,
   /// The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.
   pub theme: Option<crate::Theme>,
   /// The style of the macOS title bar.
@@ -1566,6 +1568,7 @@ impl Default for WindowConfig {
       visible_on_all_workspaces: false,
       content_protected: false,
       skip_taskbar: false,
+      window_classname: None,
       theme: None,
       title_bar_style: Default::default(),
       hidden_title: false,
@@ -2539,6 +2542,7 @@ mod build {
       let visible_on_all_workspaces = self.visible_on_all_workspaces;
       let content_protected = self.content_protected;
       let skip_taskbar = self.skip_taskbar;
+      let window_classname = opt_str_lit(self.window_classname.as_ref());
       let theme = opt_lit(self.theme.as_ref());
       let title_bar_style = &self.title_bar_style;
       let hidden_title = self.hidden_title;
@@ -2587,6 +2591,7 @@ mod build {
         visible_on_all_workspaces,
         content_protected,
         skip_taskbar,
+        window_classname,
         theme,
         title_bar_style,
         hidden_title,
diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs
index f66b119abe19..9909830eaa73 100644
--- a/crates/tauri/src/webview/webview_window.rs
+++ b/crates/tauri/src/webview/webview_window.rs
@@ -571,6 +571,13 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
     self
   }
 
+  /// Sets custom name for Windows' window class.  **Windows only**.
+  #[must_use]
+  pub fn window_classname<S: Into<String>>(mut self, classname: S) -> Self {
+    self.window_builder = self.window_builder.window_classname(classname);
+    self
+  }
+
   /// Sets whether or not the window has shadow.
   ///
   /// ## Platform-specific
diff --git a/crates/tauri/src/window/mod.rs b/crates/tauri/src/window/mod.rs
index f1292b37ab94..40de751171ff 100644
--- a/crates/tauri/src/window/mod.rs
+++ b/crates/tauri/src/window/mod.rs
@@ -645,6 +645,13 @@ impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
     self
   }
 
+  /// Sets custom name for Windows' window class. **Windows only**.
+  #[must_use]
+  pub fn window_classname<S: Into<String>>(mut self, classname: S) -> Self {
+    self.window_builder = self.window_builder.window_classname(classname);
+    self
+  }
+
   /// Sets whether or not the window has shadow.
   ///
   /// ## Platform-specific


Test patch:
diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs
index 7f51b1902077..92fcb9be64c4 100644
--- a/crates/tauri/src/test/mock_runtime.rs
+++ b/crates/tauri/src/test/mock_runtime.rs
@@ -421,6 +421,10 @@ impl WindowBuilder for MockWindowBuilder {
     self
   }
 
+  fn window_classname<S: Into<String>>(self, classname: S) -> Self {
+    self
+  }
+
   fn shadow(self, enable: bool) -> Self {
     self
   }