Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v3 feature] support for MacOS Panel #3763

Open
wants to merge 25 commits into
base: v3-alpha
Choose a base branch
from

Conversation

nixpare
Copy link

@nixpare nixpare commented Sep 20, 2024

Description

Implementation of WebviewPanel, a new component which extends the functionalities of WebviewWindow adding the capability of creating windows able to behave like panels / overlays. This PR implements the new component for MacOS.

Proposed on Issue #3760.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

How Has This Been Tested?

Created an example program to test if both WebviewWindow and WebviewPanel are working as expected (https://github.com/nixpare/wails-mac-panel).

  • Windows: no compilation error, nothing changed on WebviewWindow, panics (as expected) only when trying to use WebviewPanel
  • macOS: working as expected
  • Linux: not tested but should not differ from Windows

Test Configuration

# System
┌──────────────────────────────┐
| Name          | MacOS        |
| Version       | 14.6.1       |
| ID            | 23G93        |
| Branding      | Sonoma       |
| Platform      | darwin       |
| Architecture  | arm64        |
| Apple Silicon | true         |
| CPU           | Apple M1 Pro |
| CPU           | Unknown      |
| GPU           | Unknown      |
| Memory        | Unknown      |
└──────────────────────────────┘

# Build Environment
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Wails CLI      | v3.0.0-alpha.7                                                                                                            |
| Go Version     | go1.23.1                                                                                                                  |
| -buildmode     | exe                                                                                                                       |
| -compiler      | gc                                                                                                                        |
| CGO_CFLAGS     |                                                                                                                           |
| CGO_CPPFLAGS   |                                                                                                                           |
| CGO_CXXFLAGS   |                                                                                                                           |
| CGO_ENABLED    | 1                                                                                                                         |
| CGO_LDFLAGS    |                                                                                                                           |
| DefaultGODEBUG | asynctimerchan=1,gotypesalias=0,httpservecontentkeepheaders=1,tls3des=1,tlskyber=0,x509keypairleaf=0,x509negativeserial=1 |
| GOARCH         | arm64                                                                                                                     |
| GOARM64        | v8.0                                                                                                                      |
| GOOS           | darwin                                                                                                                    |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Dependencies
┌───────────────────────────┐
| Xcode cli tools | 2408    |
| npm             | 10.8.2  |
| *NSIS           | v3.10   |
└─ * - Optional Dependency ─┘

# Diagnosis
 SUCCESS  Your system is ready for Wails development!

Checklist:

  • I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new WebviewPanel struct for managing webview panels with customizable options.
    • Added methods for opening and enabling developer tools in macOS webview panels.
    • Enhanced Window interface with new methods for flashing, printing, and enabling/disabling windows.
    • Added a new function to retrieve screens for webview panels on macOS.
  • Improvements

    • Refactored window management to utilize a more generalized application.Window type, improving flexibility and future extensibility.
    • Consolidated window setup logic to streamline initialization and enhance maintainability.
    • Enhanced key binding management by processing key binding options for panels and windows.
  • Bug Fixes

    • Improved type safety in key event handling by ensuring methods are invoked on appropriate window types.

Copy link
Contributor

coderabbitai bot commented Sep 20, 2024

Walkthrough

The changes in this pull request involve a significant refactor of the window management system in the application, transitioning from the application.WebviewWindow type to a more generalized application.Window type. This refactor affects various functionalities related to window properties, key bindings, and event handling across multiple files, including the introduction of new methods and structures to support the management of webview panels and enhance the application's overall window handling capabilities.

Changes

Files Change Summary
v3/examples/build/main.go Updated currentWindow function parameter type from *application.WebviewWindow to application.Window.
v3/examples/events-bug/main.go Changed window parameter type in key binding function from *application.WebviewWindow to application.Window.
v3/examples/keybindings/main.go Modified KeyBindings map to accept application.Window instead of *application.WebviewWindow.
v3/examples/window/main.go Refactored to use application.Window for various window management functionalities, including hiddenWindows and ShouldClose.
v3/internal/commands/appimage_testfiles/main.go Updated window parameter types and modified logic to accommodate application.Window.
v3/pkg/application/application.go Added processKeyBindingOptions function and updated key binding methods to use application.Window.
v3/pkg/application/application_options.go Changed KeyBindings field type in Options struct from map[string]func(window *WebviewWindow) to map[string]func(window Window).
v3/pkg/application/menu_windows.go Updated parameter type in processMenu method from *WebviewWindow to Window.
v3/pkg/application/menuitem_roles.go Introduced type assertion for currentWindow in NewCutMenuItem function to ensure appropriate method calls.
v3/pkg/application/popupmenu_windows.go Modified parameter type in buildMenu method from *WebviewWindow to Window.
v3/pkg/application/screen_darwin.go Added getScreenForPanel function to retrieve screens for macOS panels.
v3/pkg/application/webview_panel.go Introduced WebviewPanel struct and associated methods for managing webview panels.
v3/pkg/application/webview_panel_darwin.go Added macOS-specific functionality for managing webview panels.
v3/pkg/application/webview_window.go Renamed processKeyBindingOptions to windowProcessKeyBindingOptions.
v3/pkg/application/webview_window_darwin.go Added initialization methods for NSWindow and NSPanel, streamlining window setup.

Possibly related issues

  • wailsapp/wails#3760: The changes introduce a WebviewPanel that inherits functionality from WebviewWindow, aligning with the request for support for macOS panels.

Possibly related PRs

  • [mac] fix always on top during window creation #3841: This PR addresses the AlwaysOnTop feature during window creation, which is directly related to the changes in the main PR that involve updating the currentWindow function to use the application.Window type, affecting how window properties are managed.
  • Fix build assets update #3901: This PR includes fixes for the AlwaysOnTop feature on macOS, which aligns with the changes in the main PR that modify window management functionalities, including setting window properties.

🐇 In the garden, windows bloom,
With panels bright, dispelling gloom.
The keys now dance, the bindings sing,
A joyful hop, a wondrous spring!
With every click, the magic flows,
In rabbit's realm, where progress grows! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 35

Outside diff range and nitpick comments (17)
v3/pkg/application/webview_panel_linux.go (1)

3-5: Enhance the error message to provide more context.

Consider updating the error message to provide more information about the current state of the Linux implementation and any potential workarounds or alternatives.

Here's a suggestion:

-	panic("Linux Panel not implemented")
+	panic("WebviewPanel is currently not supported on Linux. This feature is specific to MacOS at the moment. Please use WebviewWindow for cross-platform compatibility or check the project roadmap for Linux support.")
v3/pkg/application/webview_panel_windows.go (1)

4-4: Enhance the panic message to provide more context.

Consider updating the panic message to provide more information about the expected behavior and guide the user on the supported platforms.

Apply this diff to enhance the panic message:

-	panic("Windows Panel not implemented")
+	panic("WebviewPanel is not supported on Windows. It is currently only implemented for MacOS.")
v3/pkg/application/webview_responder_darwin.h (1)

1-15: Consider adding documentation comments.

While the code is well-structured and follows conventions, it would be beneficial to add documentation comments to describe the purpose and behavior of the WebviewResponder interface and its methods. This will improve the code's readability and maintainability.

v3/pkg/application/webview_panel_darwin.go (3)

78-86: Enhance error logging in handleKeyEvent

When logging the error for an unparsed accelerator, include the problematic acceleratorString to aid in debugging.

Apply this diff to improve the error message:

if err != nil {
-   globalApplication.error("unable to parse accelerator: %s", err.Error())
+   globalApplication.error("unable to parse accelerator '%s': %s", acceleratorString, err.Error())
    return
}

31-36: Add documentation for macosWebviewPanel struct

The macosWebviewPanel struct lacks comments explaining its purpose and usage. Adding documentation will improve code maintainability and assist other developers.


37-49: Document the newPanelImpl function

The function newPanelImpl could benefit from a comment describing its functionality, parameters, and return value.

v3/pkg/application/webview_responder_darwin.m (1)

16-16: Address the TODO: Investigate why ctrl+l never reaches keyDown

The TODO comment indicates that the ctrl+l key combination is not reaching the keyDown: method. This might be due to certain key events being intercepted before they reach your responder. Investigate and resolve this issue to ensure all intended key events are properly handled.

Do you want me to assist in identifying the cause or suggest potential solutions?

v3/internal/commands/appimage_testfiles/main.go (2)

Line range hint 69-79: Potential issue with casting and comparing window interfaces.

In the ShouldClose function, you're casting window *application.WebviewWindow to application.Window for use in lo.Contains and lo.Without:

if !lo.Contains(hiddenWindows, application.Window(window)) {
    // ...
}
hiddenWindows = lo.Without(hiddenWindows, application.Window(window))

Be cautious with this approach, as comparing interfaces that contain pointers may not yield the expected results due to interface value comparisons in Go. This could lead to logic errors in window management.

Consider one of the following solutions:

  • Option 1: Change the ShouldClose function to accept window application.Window if possible.
  • Option 2: Use a unique identifier for each window (e.g., assign an ID) and compare these identifiers instead.

232-234: Update dialog titles for consistency with the window type.

The dialog titles still refer to "Current WebviewWindow" even though the code now uses application.Window. For clarity and consistency, consider updating the titles to reflect the generic window type.

Apply this diff to update the dialog titles:

-application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage(...)
+application.InfoDialog().SetTitle("Current Window Size").SetMessage(...)

-application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage(...)
+application.InfoDialog().SetTitle("Current Window Position").SetMessage(...)

Also applies to: 263-265, 282-284

v3/pkg/application/webview_window_darwin.go (3)

445-446: Avoid Multiple Conversions of macOptions.Appearance

At line 445, macOptions.Appearance is converted to a string and then to a C string within the same line.

For clarity and efficiency, store the intermediate string conversion result:

if macOptions.Appearance != "" {
+   appearanceStr := string(macOptions.Appearance)
+   cAppearance := C.CString(appearanceStr)
    C.windowSetAppearanceTypeByName(w.nsWindow, cAppearance)
+   C.free(unsafe.Pointer(cAppearance))
}

404-404: Remove or Clarify Commented-Out Code

Line 404 contains commented-out code: //w.setZoom(options.Zoom)

If this code is no longer needed, consider removing it. If it's retained for future use, add a comment explaining why it's commented out.


410-417: Empty Case in Switch Statement

In the switch statement for macOptions.Backdrop, the MacBackdropNormal case is empty.

To improve code readability, add a comment indicating that no action is needed for this case.

case MacBackdropNormal:
    // No action needed
}
v3/examples/window/main.go (2)

200-200: Remove redundant type conversions to application.Window

The variable window is already of type application.Window, so casting it with application.Window(window) is unnecessary. Removing these redundant conversions will enhance code readability.

Apply the following changes:

- if !lo.Contains(hiddenWindows, application.Window(window)) {
+ if !lo.Contains(hiddenWindows, window) {
- hiddenWindows = lo.Without(hiddenWindows, application.Window(window))
+ hiddenWindows = lo.Without(hiddenWindows, window)

Also applies to: 210-210


Line range hint 42-42: Update log message to reflect new window type

The log message still references WebviewWindow, but the code now uses application.Window. To maintain consistency and avoid confusion, consider updating the log message.

Apply the following change:

- println("Current WebviewWindow is nil")
+ println("Current window is nil")
v3/pkg/application/application.go (1)

165-178: Include problematic key in error messages for better debugging

In processKeyBindingOptions, when logging an error due to an invalid key binding, it would be helpful to include the specific key that caused the error. This provides clearer context when debugging issues with key bindings.

Apply this diff to enhance the error message:

if err != nil {
-    globalApplication.error("Invalid keybinding: %s", err.Error())
+    globalApplication.error("Invalid keybinding for key '%s': %s", key, err.Error())
    continue
}
v3/pkg/application/webview_window_bindings_darwin.m (2)

47-50: Consider making the corner radius configurable for frameless windows.

The corner radius is hardcoded to 8.0 when frameless is true. To provide flexibility and accommodate different design requirements, consider making the corner radius a configurable parameter.


774-775: Remove redundant call to makeKeyWindow to streamline window focusing.

In the windowFocus function, both [nsWindow makeKeyAndOrderFront:nil]; and [nsWindow makeKeyWindow]; are called. Since makeKeyAndOrderFront: already makes the window key and brings it to the front, the subsequent call to makeKeyWindow may be unnecessary.

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 860d02d and 76558b9.

Files selected for processing (26)
  • v3/examples/build/main.go (3 hunks)
  • v3/examples/events-bug/main.go (1 hunks)
  • v3/examples/keybindings/main.go (1 hunks)
  • v3/examples/window/main.go (7 hunks)
  • v3/internal/commands/appimage_testfiles/main.go (8 hunks)
  • v3/pkg/application/application.go (12 hunks)
  • v3/pkg/application/application_options.go (1 hunks)
  • v3/pkg/application/menu_windows.go (1 hunks)
  • v3/pkg/application/menuitem_roles.go (1 hunks)
  • v3/pkg/application/popupmenu_windows.go (1 hunks)
  • v3/pkg/application/screen_darwin.go (1 hunks)
  • v3/pkg/application/webview_panel.go (1 hunks)
  • v3/pkg/application/webview_panel_darwin.go (1 hunks)
  • v3/pkg/application/webview_panel_linux.go (1 hunks)
  • v3/pkg/application/webview_panel_options.go (1 hunks)
  • v3/pkg/application/webview_panel_windows.go (1 hunks)
  • v3/pkg/application/webview_responder_darwin.h (1 hunks)
  • v3/pkg/application/webview_responder_darwin.m (1 hunks)
  • v3/pkg/application/webview_window.go (1 hunks)
  • v3/pkg/application/webview_window_bindings_darwin.h (1 hunks)
  • v3/pkg/application/webview_window_bindings_darwin.m (1 hunks)
  • v3/pkg/application/webview_window_darwin.go (2 hunks)
  • v3/pkg/application/webview_window_darwin.h (2 hunks)
  • v3/pkg/application/webview_window_darwin.m (2 hunks)
  • v3/pkg/application/webview_window_darwin_dev.go (1 hunks)
  • v3/pkg/application/window.go (2 hunks)
Additional comments not posted (53)
v3/pkg/application/webview_responder_darwin.h (3)

1-2: Header guards are properly defined.

The header file uses the #ifndef and #define directives to prevent multiple inclusions of the header file. The header guard macro is unique and follows the naming convention of the file.

Also applies to: 15-15


4-4: Appropriate header inclusion.

The code includes the Cocoa/Cocoa.h header, which is necessary for using Cocoa framework classes and types in the interface definition.


6-13: WebviewResponder interface is well-defined.

  • The interface extends the NSResponder class, which is appropriate for handling events in a Cocoa application.
  • It declares a property w of type NSWindow * to store a reference to the associated window.
  • The initAttachToWindow: method is declared for initializing the responder and attaching it to a window.
  • The keyDown: method is overridden to handle key down events.
  • The keyStringFromEvent: and specialKeyStringFromEvent: methods are utility methods for converting key events to strings.
  • The method names follow the Objective-C naming conventions and use appropriate parameter types.
v3/pkg/application/webview_window_darwin.h (5)

1-2: LGTM!

The header guard change aligns with the file name and follows a consistent naming convention.


6-7: LGTM!

The inclusion of the new header file webview_responder_darwin.h is necessary for the WebviewResponder class.


33-33: LGTM!

The header guard closing statement has been updated to match the new header guard name, which is consistent with the change at the beginning of the file.


14-15: Verify the usage of the new initialization methods.

The addition of the initAsWindow:styleMask:backing:defer: and initAsPanel:styleMask:backing:defer: methods provides a clearer way to initialize WebviewWindow as either a window or a panel. However, the removal of the previous initWithContentRect:styleMask:backing:defer: method is a breaking change.

Please ensure that all occurrences of the previous initialization method have been updated to use the new methods and that the new methods are being used correctly throughout the codebase.

Run the following script to verify the usage of the initialization methods:

#!/bin/bash
# Description: Verify the usage of the new initialization methods.

# Test 1: Search for occurrences of the previous initialization method. 
# Expect: No occurrences found.
rg --type objective-c $'initWithContentRect:styleMask:backing:defer:'

# Test 2: Search for occurrences of the new `initAsWindow` method. 
# Expect: Only valid usages found.
rg --type objective-c $'initAsWindow:styleMask:backing:defer:'

# Test 3: Search for occurrences of the new `initAsPanel` method. 
# Expect: Only valid usages found.
rg --type objective-c $'initAsPanel:styleMask:backing:defer:'

8-12: Verify the impact of the class inheritance change and new properties.

The WebviewWindow class now inherits from NSObject instead of NSWindow, which is a breaking change. Please ensure that this change has been properly handled throughout the codebase and does not introduce any compatibility issues.

Additionally, verify that the addition of the w and responder properties does not introduce any unintended side effects or memory management issues.

Run the following script to verify the usage of WebviewWindow:

Verification successful

Verification complete: Class inheritance change implemented consistently, but 'responder' property not found.

The change from WebviewWindow inheriting NSWindow to having an NSWindow property w has been implemented consistently throughout the codebase. This architectural change appears to be properly handled, with no obvious issues or inconsistencies found.

However, the responder property mentioned in the original review comment was not found in the codebase. It's possible that this property was removed or renamed during the refactoring process.

Key findings:

  • WebviewWindow now wraps an NSWindow instance instead of subclassing it.
  • The w property of type NSWindow is widely used across the codebase.
  • No remaining direct inheritances from NSWindow for WebviewWindow were found.
  • Initialization methods for WebviewWindow are present and consistent with the new structure.

It's recommended to double-check the status of the responder property, as it was not found in the current codebase.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of `WebviewWindow` after the class inheritance change.

# Test 1: Search for occurrences of `WebviewWindow` being treated as an `NSWindow`. 
# Expect: No occurrences found.
rg --type objective-c $'WebviewWindow \*\).*NSWindow'

# Test 2: Search for occurrences of `WebviewWindow` being initialized without providing an `NSWindow`. 
# Expect: No occurrences found.
rg --type objective-c $'WebviewWindow alloc\] init'

# Test 3: Search for occurrences of accessing the `w` property of `WebviewWindow`. 
# Expect: Only valid accesses found.
rg --type objective-c $'WebviewWindow.*\.w'

# Test 4: Search for occurrences of accessing the `responder` property of `WebviewWindow`. 
# Expect: Only valid accesses found.
rg --type objective-c $'WebviewWindow.*\.responder'

Length of output: 591


Script:

#!/bin/bash
# Description: Find Objective-C files and verify WebviewWindow usage

# Find Objective-C file extensions
echo "Objective-C file extensions:"
fd -e h -e m -e mm --type f

# Search for WebviewWindow in all files (ignoring case)
echo -e "\nWebviewWindow usage:"
rg -i "WebviewWindow"

# Search for specific patterns related to WebviewWindow
echo -e "\nWebviewWindow inheritance and property access:"
rg -i "WebviewWindow.*NSWindow"
rg -i "WebviewWindow.*\.w"
rg -i "WebviewWindow.*\.responder"

Length of output: 119023


Script:

#!/bin/bash
# Description: Further verify WebviewWindow changes and search for 'responder'

# Search for 'responder' in relation to WebviewWindow
echo "Searching for 'responder' in relation to WebviewWindow:"
rg -i "WebviewWindow.*responder"

# Search for potential issues or inconsistencies
echo -e "\nSearching for potential issues or inconsistencies:"
rg -i "WebviewWindow.*NSWindow" -A 5 -B 5

# Check for any remaining direct inheritance from NSWindow
echo -e "\nChecking for any remaining direct inheritance from NSWindow:"
rg -i "WebviewWindow\s*:\s*NSWindow"

# Search for WebviewWindow initialization
echo -e "\nSearching for WebviewWindow initialization:"
rg -i "WebviewWindow.*init"

Length of output: 41816

v3/examples/events-bug/main.go (1)

16-17: Verify that the Window type provides all the necessary functionality.

The change from *application.WebviewWindow to application.Window allows for more flexibility in the key binding function, as it can now accept any type that satisfies the Window interface or extends the Window base type.

However, please ensure that the Window type provides all the necessary methods and properties for the intended use case within the key binding function. If any WebviewWindow-specific functionality is required, you may need to update the function body accordingly.

v3/pkg/application/webview_window_darwin_dev.go (2)

59-61: LGTM!

The addition of the openDevTools method to the macosWebviewPanel struct is a positive change that enhances its functionality and aligns its behavior with the macosWebviewWindow struct. The implementation correctly passes the nsPanel pointer to the C function.


63-65: LGTM!

The addition of the enableDevTools method to the macosWebviewPanel struct is a positive change that enhances its functionality and aligns its behavior with the macosWebviewWindow struct. The implementation correctly passes the nsPanel pointer to the C function.

v3/pkg/application/webview_panel_options.go (3)

3-15: LGTM!

The WebviewPanelOptions struct is well-defined, with good reusability by embedding WebviewWindowOptions, and useful customization options through the Floating, ShouldClose, and KeyBindings fields.


17-19: LGTM!

Providing default options through WebviewPanelDefaults is a good practice to ensure consistent behavior and reduce boilerplate. Initializing WebviewWindowOptions with WebviewWindowDefaults ensures that the default window options are used as a starting point.


21-47: LGTM!

The processKeyBindingOptionsForPanel function is well-implemented, with good handling of both panel and window key bindings. Parsing the keys into accelerators ensures that the key bindings are valid and consistent. Wrapping the window key binding callbacks to convert the WebviewWindow to a WebviewPanel is a good way to reuse existing key bindings. Logging invalid key bindings and skipping them is a good way to handle errors gracefully.

v3/pkg/application/window.go (3)

25-25: LGTM!

The Flash method is a valuable addition to the Window interface, providing a clear and concise way to enable or disable the flash effect within the window. The method signature and parameter naming are appropriate.


51-51: LGTM!

The Print method is a useful addition to the Window interface, providing a straightforward way to handle printing functionality. Returning an error is a good practice to handle and propagate any failures during the printing operation. The method signature and error handling are appropriate.


58-58: LGTM!

The SetEnabled method is a valuable addition to the Window interface, providing a clear and concise way to enable or disable the window itself. The method signature and parameter naming are appropriate.

v3/pkg/application/menu_windows.go (1)

76-78: LGTM! The change aligns with the PR objectives.

The change in the parameter type of the anonymous function from *WebviewWindow to Window broadens the compatibility of the function with different window types. This change enables the processMenu method to work with both WebviewWindow and the new WebviewPanel component, which is the desired behavior as per the PR objectives.

The change is unlikely to affect the existing functionality of WebviewWindow as it also implements the Window interface.

v3/pkg/application/screen_darwin.go (1)

176-179: LGTM!

The new getScreenForPanel function is implemented correctly and follows the existing pattern for retrieving screens. It expands the capability to handle panels, complementing the existing functionality for windows and systrays.

The function:

  • Takes a pointer to a macosWebviewPanel as an argument.
  • Calls the existing C function getScreenForWindow with the nsPanel property to retrieve the corresponding screen.
  • Converts the result to a Screen type using the existing cScreenToScreen function.

The function signature and usage of the C function are consistent with the existing getScreenForWindow function used for macosWebviewWindow.

v3/pkg/application/popupmenu_windows.go (1)

126-126: Verify that all the code has been updated to pass a type that implements the Window interface.

The change in the function signature from *WebviewWindow to Window makes the function more flexible as it can now work with any type that implements the Window interface. This is a good change.

However, this is a breaking change. Please verify that all the code that was passing a *WebviewWindow to this function has been updated to pass a type that implements the Window interface.

Run the following script to verify the usage of the addKeyBinding function:

Verification successful

Verification successful: addKeyBinding usage is consistent with the new Window interface

The review of globalApplication.addKeyBinding usage across the codebase confirms that the change from *WebviewWindow to Window has been consistently applied. All occurrences now use func(w Window) { ... }, which aligns with the new interface-based approach. This verification indicates that the breaking change has been properly addressed throughout the relevant parts of the codebase.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all calls to `addKeyBinding` pass a type that implements the `Window` interface.

# Test: Search for the function usage. Expect: No occurrences of `*WebviewWindow` being passed.
rg --type go -A 5 $'globalApplication\.addKeyBinding'

Length of output: 814

v3/pkg/application/menuitem_roles.go (1)

50-55: LGTM!

The type switch enhances type safety by ensuring that the cut method is invoked only on the appropriate types. It correctly handles the new *WebviewPanel type introduced in the PR and exhaustively handles all possible types of currentWindow, preventing potential runtime errors.

v3/examples/build/main.go (12)

Line range hint 32-38: LGTM!

The change in the currentWindow function signature to accept a parameter of type func(window application.Window) instead of func(window *application.WebviewWindow) is a good move towards using interfaces for better abstraction and flexibility.

This suggests that the application.Window interface has been introduced to provide a more generic way of interacting with windows, and application.WebviewWindow likely implements this interface. The unchanged function body indicates that the application.Window interface likely provides the same methods as application.WebviewWindow.


121-124: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature.

It also confirms that the application.Window interface provides a SetSize method, which was previously available on *application.WebviewWindow.


127-130: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segment.

It further confirms that the application.Window interface provides a SetSize method, which was previously available on *application.WebviewWindow.


132-135: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It confirms that the application.Window interface provides a SetMinSize method, which was previously available on *application.WebviewWindow.


137-141: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It confirms that the application.Window interface provides SetMaximiseButtonState and SetMaxSize methods, which were previously available on *application.WebviewWindow.


143-147: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It confirms that the application.Window interface provides a Size method, which was previously available on *application.WebviewWindow.


150-153: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It further confirms that the application.Window interface provides a SetMinSize method, which was previously available on *application.WebviewWindow.


156-160: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It further confirms that the application.Window interface provides SetMaxSize and SetMaximiseButtonState methods, which were previously available on *application.WebviewWindow.


163-166: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It confirms that the application.Window interface provides a SetRelativePosition method, which was previously available on *application.WebviewWindow.


168-171: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It further confirms that the application.Window interface provides a SetRelativePosition method, which was previously available on *application.WebviewWindow.


174-178: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It confirms that the application.Window interface provides a RelativePosition method, which was previously available on *application.WebviewWindow.


181-184: LGTM!

The change in the function literal passed to currentWindow to accept a parameter of type application.Window instead of *application.WebviewWindow is consistent with the updated currentWindow function signature and the previous code segments.

It confirms that the application.Window interface provides a Center method, which was previously available on *application.WebviewWindow.

v3/pkg/application/webview_window.go (2)

260-260: LGTM!

The function name change is consistent and the key bindings are correctly assigned to result.keyBindings.


Line range hint 266-280: LGTM!

The function has been renamed but the logic remains unchanged. The key bindings are correctly parsed and added to the result map.

v3/pkg/application/webview_panel_darwin.go (3)

4-6: Verify the minimum macOS version in CFLAGS

The #cgo CFLAGS sets -mmacosx-version-min=10.13. Ensure that this minimum macOS version aligns with the features used in this implementation and the project's supported macOS versions.


45-47: Confirm the correctness of execJS invocation

The call to result.execJS(runtime.Core()) may need verification. Ensure that execJS is intended to be called with runtime.Core() as an argument and that this aligns with its definition.


73-74: Ensure setFloating is called after nsPanel is initialized

The method p.setFloating(options.Floating) is called immediately after setting up the panel. Confirm that p.nsPanel is properly initialized before this call to avoid potential nil pointer dereferences.

v3/pkg/application/webview_window_bindings_darwin.h (2)

93-96: Verify the correct parameter type for setButtonState

The function setButtonState takes a void *button parameter, whereas related functions like setMinimiseButtonState use void *window. Ensure that setButtonState is intended to operate on a button object and that the parameter type is correct.

Please confirm whether setButtonState should be:

-void setButtonState(void *button, int state);
+void setButtonState(void *window, int state);

Or if it should indeed operate on a separate button object.


107-107: Include guard correctly closed

The include guard #endif // WEBVIEW_WINDOW_BINDINGS_DARWIN is properly placed, ensuring the header is correctly encapsulated.

v3/internal/commands/appimage_testfiles/main.go (2)

31-31: Verify the change from pointer to interface in hiddenWindows.

The variable hiddenWindows has been updated from []*application.WebviewWindow to []application.Window. Ensure that application.Window correctly represents the windows you intend to store and that all methods and comparisons used with this slice function as expected.


33-36: Update to currentWindow function enhances flexibility.

Changing the currentWindow helper function to accept application.Window instead of *application.WebviewWindow allows for greater flexibility, especially if application.Window is an interface that multiple window types implement.

v3/pkg/application/webview_window_darwin.go (1)

419-422: Ensure Proper Handling of Default Window Level

The check for an empty macOptions.WindowLevel assigns MacWindowLevelNormal if it's empty.

Confirm that comparing macOptions.WindowLevel to an empty string ("") is appropriate. If WindowLevel is of a specific type or has default values, ensure this logic correctly handles all cases.

v3/pkg/application/webview_window_darwin.m (1)

60-60: Verification Needed: Ensure window.w Access Is Valid

In the startDrag: method, you've changed the call to [window.w performWindowDragWithEvent:self.leftMouseEvent];. Verify that window.w is properly initialized and that window is indeed an instance of WebviewWindow with a valid w property.

Run the following script to ensure that all instances of window passed to startDrag: are of type WebviewWindow:

This script searches for calls to startDrag: and provides context to help confirm that the window parameter is correctly typed.

Verification successful

To gather more information and verify our concerns, let's run the following scripts:


Verification Successful: window.w Access Is Valid

The code review has been verified. The window parameter passed to startDrag: is indeed an instance of WebviewWindow, and it has a valid w property. The changes made in the startDrag: method are consistent with the rest of the codebase.

Key findings:

  • The startDrag: method is implemented to accept a WebviewWindow* parameter.
  • WebviewWindow instances consistently have a w property used throughout the code.
  • The call to startDrag: in webview_window_bindings_darwin.m passes a WebviewWindow* object.

No further action is required regarding this verification.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all calls to startDrag: pass a WebviewWindow instance.

# Search for all calls to startDrag: and check the object type.
rg --type objc '\[.* startDrag:.*\]' -A 2

Length of output: 245


Script:

#!/bin/bash
# Search for webviewWindow declaration/initialization
rg --type objc 'webviewWindow' v3/pkg/application/webview_window_bindings_darwin.m -C 2

# Look for WebviewWindow class implementation
ast-grep --lang objc --pattern 'interface WebviewWindow : $_ {
  $$$
}'

# Check windowDelegate implementation and startDrag: method
rg --type objc 'windowDelegate' v3/pkg/application/webview_window_bindings_darwin.m -C 2
rg --type objc 'startDrag:' -C 2

Length of output: 6785

v3/examples/window/main.go (1)

38-38: Verify type compatibility in currentWindow function

The currentWindow function now accepts a parameter of type application.Window instead of *application.WebviewWindow. Please ensure that all methods invoked on window within this function and its callbacks are part of the application.Window interface and that no pointer-specific functionality is required.

To confirm, you can run the following script to check for any methods called on window that might not be defined in the application.Window interface:

This script will list all unique methods called on window, allowing you to cross-reference them with the application.Window interface.

Verification successful

Type compatibility in currentWindow function verified

The change from *application.WebviewWindow to application.Window in the currentWindow function is compatible with all operations performed on the window parameter. All method calls observed are consistent with what would be expected from a general window interface, and no pointer-specific functionality is used.

Methods called on the window parameter include operations for size, position, state management, and various window-specific actions. These are all appropriate for an application.Window interface.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all methods called on `window` are defined in `application.Window` interface.

# Search for method calls on `window` in `main.go`.
rg --type go --no-heading -A 1 'func\(w application\.Window\)' main.go | grep '\.w\.' | awk '{print $2}' | sort | uniq

Length of output: 168


Script:

#!/bin/bash

# Find the correct location of main.go
file_path=$(fd -t f main.go)

if [ -z "$file_path" ]; then
  echo "main.go not found in the repository"
  exit 1
fi

echo "File found at: $file_path"

# Display the content of the file to verify it's the correct one
echo "File content:"
cat "$file_path"

echo "Analyzing method calls on window parameter:"
rg --type go --no-heading -A 1 'func\(w application\.Window\)' "$file_path" | grep '\.w\.' | awk '{print $2}' | sort | uniq

Length of output: 26515


Script:

#!/bin/bash

echo "File content:"
cat v3/examples/window/main.go

echo -e "\nAnalyzing method calls on window parameter:"
rg --type go --no-heading -A 10 'func\(.*application\.Window\)' v3/examples/window/main.go | rg '\.(\w+)\('

echo -e "\nListing all method calls on 'w' parameter:"
rg --type go --no-heading 'w\.\w+' v3/examples/window/main.go | awk '{print $2}' | sort | uniq

Length of output: 26292

v3/pkg/application/application.go (5)

340-341: Thread-safe management of keyBindings map using sync.RWMutex

Good use of RWMutex to manage concurrent access to the keyBindings map. This ensures that key bindings are safely read and modified across multiple goroutines.


420-423: Initialization of key binding structures

The initialization of a.keyBindings and other related fields in the init function is appropriate and ensures that the application is correctly set up before use.


Line range hint 755-766: Correctly updated CurrentWindow to return Window interface

The CurrentWindow method has been updated to return the Window interface instead of *WebviewWindow. This change aligns with the shift towards using the more general Window type and enhances flexibility.


Line range hint 965-974: Ensure key binding callbacks are thread-safe

The processKeyBinding method executes callbacks in separate goroutines to prevent blocking. Ensure that the callback functions provided in keyBindings are thread-safe, as they may be executed concurrently.

Verify that all key binding callbacks are thread-safe by reviewing their implementations. If necessary, add synchronization mechanisms within the callbacks to handle shared resources safely.


985-988: Proper synchronization when adding and removing key bindings

The use of write locks in addKeyBinding ensures thread-safe modification of the keyBindings map. This is appropriate for preventing race conditions during updates.

v3/pkg/application/webview_window_bindings_darwin.m (3)

14-17: Verify window style mask settings for frameless windows.

When frameless is true, the style mask is set to NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable. Ensure that this style mask includes all necessary window styles for frameless windows to function correctly and that it aligns with the desired window behavior.


628-629: Verify setting window size constraints to zero in windowSetFullScreen.

In the windowSetFullScreen function, windowSetMaxSize(nsWindow, 0, 0); and windowSetMinSize(nsWindow, 0, 0); set the maximum and minimum window sizes to zero. This might lead to unexpected behavior or restrict user interactions. Confirm that this is the intended effect when entering full-screen mode.


85-86: Ensure compatibility when setting fraudulentWebsiteWarningEnabled.

The property fraudulentWebsiteWarningEnabled is set within an if (@available(macOS 10.15, *)) block. Verify that this property exists and functions as expected on all targeted macOS versions to prevent potential runtime issues on unsupported versions.

v3/examples/keybindings/main.go Show resolved Hide resolved
v3/pkg/application/application_options.go Show resolved Hide resolved
p.setFloating(options.Floating)
})
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests are missing for macosWebviewPanel

No unit tests are provided for the new macosWebviewPanel implementation. Adding tests will help ensure that the panel behaves as expected and will prevent future regressions.

v3/pkg/application/webview_panel_darwin.go Show resolved Hide resolved
v3/pkg/application/webview_panel_darwin.go Show resolved Hide resolved
v3/pkg/application/application.go Outdated Show resolved Hide resolved
v3/pkg/application/webview_window_bindings_darwin.m Outdated Show resolved Hide resolved
v3/pkg/application/webview_window_bindings_darwin.m Outdated Show resolved Hide resolved
Comment on lines 195 to 198
void windowSetTitle(void* nsWindow, char* title) {
NSString* nsTitle = [NSString stringWithUTF8String:title];
[((WebviewWindow*)nsWindow).w setTitle:nsTitle];
free(title);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review the use of free(title); to prevent memory management issues.

After setting the window title, the code calls free(title);. If title was not dynamically allocated using malloc or a similar function, freeing it can lead to undefined behavior or crashes. Verify the ownership and allocation of title. If it's not necessary to free title here, remove the free(title); line.

Apply this diff to fix the issue:

void windowSetTitle(void* nsWindow, char* title) {
    NSString* nsTitle = [NSString stringWithUTF8String:title];
    [((WebviewWindow*)nsWindow).w setTitle:nsTitle];
-   free(title);
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void windowSetTitle(void* nsWindow, char* title) {
NSString* nsTitle = [NSString stringWithUTF8String:title];
[((WebviewWindow*)nsWindow).w setTitle:nsTitle];
free(title);
void windowSetTitle(void* nsWindow, char* title) {
NSString* nsTitle = [NSString stringWithUTF8String:title];
[((WebviewWindow*)nsWindow).w setTitle:nsTitle];
}

Comment on lines 502 to 509
void windowSetAppearanceTypeByName(void* nsWindow, const char *appearanceName) {
// set window appearance type by name
// Convert appearance name to NSString
NSString* appearanceNameString = [NSString stringWithUTF8String:appearanceName];
// Set appearance
[((WebviewWindow*)nsWindow).w setAppearance:[NSAppearance appearanceNamed:appearanceNameString]];

free((void*)appearanceName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid freeing appearanceName to prevent potential crashes.

The function windowSetAppearanceTypeByName calls free((void*)appearanceName); after setting the window appearance. If appearanceName was not dynamically allocated, this can cause crashes. Ensure that memory management is handled correctly. If appearanceName doesn't need to be freed here, remove the free statement.

Apply this diff to fix the issue:

void windowSetAppearanceTypeByName(void* nsWindow, const char *appearanceName) {
    // Convert appearance name to NSString
    NSString* appearanceNameString = [NSString stringWithUTF8String:appearanceName];
    // Set appearance
    [((WebviewWindow*)nsWindow).w setAppearance:[NSAppearance appearanceNamed:appearanceNameString]];

-   free((void*)appearanceName);
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void windowSetAppearanceTypeByName(void* nsWindow, const char *appearanceName) {
// set window appearance type by name
// Convert appearance name to NSString
NSString* appearanceNameString = [NSString stringWithUTF8String:appearanceName];
// Set appearance
[((WebviewWindow*)nsWindow).w setAppearance:[NSAppearance appearanceNamed:appearanceNameString]];
free((void*)appearanceName);
void windowSetAppearanceTypeByName(void* nsWindow, const char *appearanceName) {
// set window appearance type by name
// Convert appearance name to NSString
NSString* appearanceNameString = [NSString stringWithUTF8String:appearanceName];
// Set appearance
[((WebviewWindow*)nsWindow).w setAppearance:[NSAppearance appearanceNamed:appearanceNameString]];
}

@leaanthony
Copy link
Member

Thanks for taking the time to open this. The rabbit has picked up on a few points to address. Would it be possible to also add an example as that really helps testing

@nixpare
Copy link
Author

nixpare commented Sep 20, 2024

Thanks for taking the time to open this. The rabbit has picked up on a few points to address. Would it be possible to also add an example as that really helps testing

At this repo (https://github.com/nixpare/wails-mac-panel) you will find the "hello world" of the project modified to use the WebviewPanel, which is created/focused/hidden with a global hotkey (using an external library).
I know its not a lot, but that's what I have for know, if you want me to help you fix some of the problems pointed out by rabbit or write some test, just let me know

@leaanthony
Copy link
Member

Yeah I think some of the points raised are worth addressing 👍

@nixpare
Copy link
Author

nixpare commented Sep 21, 2024

I will fix all the problems i can, but on those related to memory safety of the objective-c bindings (inherited by v3.0.0-alpha.7) i don't feel confident, considering i'm also completely new to this language

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 76558b9 and 2ede8e2.

Files selected for processing (10)
  • v3/pkg/application/application.go (12 hunks)
  • v3/pkg/application/application_options.go (1 hunks)
  • v3/pkg/application/webview_panel.go (1 hunks)
  • v3/pkg/application/webview_panel_darwin.go (1 hunks)
  • v3/pkg/application/webview_panel_options.go (1 hunks)
  • v3/pkg/application/webview_window.go (2 hunks)
  • v3/pkg/application/webview_window_bindings_darwin.h (1 hunks)
  • v3/pkg/application/webview_window_bindings_darwin.m (1 hunks)
  • v3/pkg/application/webview_window_darwin.go (4 hunks)
  • v3/pkg/application/webview_window_darwin.m (2 hunks)
Files skipped from review as they are similar to previous changes (7)
  • v3/pkg/application/application.go
  • v3/pkg/application/application_options.go
  • v3/pkg/application/webview_panel.go
  • v3/pkg/application/webview_panel_darwin.go
  • v3/pkg/application/webview_panel_options.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/webview_window_darwin.m
Additional context used
Learnings (1)
v3/pkg/application/webview_window.go (1)
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/examples/keybindings/main.go:16-17
Timestamp: 2024-09-20T23:34:29.841Z
Learning: In the codebase, `application.Options.KeyBindings` uses the `application.Window` type, whereas `application.WebviewWindowOptions.KeyBindings` uses `*application.WebviewWindow`. This is intentional and acceptable.
Additional comments not posted (12)
v3/pkg/application/webview_window_bindings_darwin.m (11)

3-114: LGTM!

The function correctly creates a new window or panel based on the provided parameters. The window delegate, content view, and webview are set up properly. The function also configures the window and webview based on the provided parameters.


116-165: LGTM!

The function correctly retrieves the window delegate and style mask. It then checks the style mask against various window style constants and prints the corresponding style name. The logic is straightforward and there are no apparent issues.


168-175: LGTM!

The function correctly retrieves the window delegate and sets the invisibleTitleBarHeight property to the provided height value. The logic is straightforward and there are no apparent issues.


177-181: LGTM!

The function correctly sets the window's opaque property to NO and the backgroundColor to clearColor to make the window transparent. The logic is straightforward and there are no apparent issues.


183-187: LGTM!

The function correctly retrieves the window delegate and sets the invisibleTitleBarHeight property to the provided height value. The logic is straightforward and there are no apparent issues.


190-195: LGTM!

The function correctly converts the provided title string to an NSString and sets it as the window's title. It also frees the title string after setting the window title to avoid memory leaks. The logic is straightforward and there are no apparent issues.


197-204: LGTM!

The function correctly calculates the content size based on the provided width and height values. It then sets the window's contentSize and frame properties to resize the window with animation. The logic is straightforward and there are no apparent issues.


206-210: LGTM!

The function correctly sets the window's level property based on the value of alwaysOnTop parameter. If alwaysOnTop is true, it sets the window to be always on top by using NSFloatingWindowLevel. If alwaysOnTop is false, it sets the window to be a normal window by using NSNormalWindowLevel. The logic is straightforward and there are no apparent issues.


212-219: LGTM!

These small functions correctly set the window's level property to various predefined window level constants. Each function is straightforward and sets the window level to a specific constant such as NSNormalWindowLevel, NSFloatingWindowLevel, NSPopUpMenuWindowLevel, NSMainMenuWindowLevel, NSStatusWindowLevel, NSModalPanelWindowLevel, NSScreenSaverWindowLevel, or NSTornOffMenuWindowLevel. There are no apparent issues with these functions.


221-229: LGTM!

The function correctly loads a URL in the window's webview. It converts the provided url string to an NSURL object and creates an NSURLRequest object with the URL. It then loads the URL request in the window's webview using the loadRequest method. The function also frees the url string after loading the URL to avoid memory leaks. The logic is straightforward and there are no apparent issues.


231-242: LGTM!

The function correctly sets whether the window is resizable or not. It retrieves the window's current style mask and modifies it based on the value of the resizable parameter. If resizable is true, it adds the NSWindowStyleMaskResizable flag to the style mask. If resizable is false, it removes the NSWindowStyleMaskResizable flag from the style mask. It then sets the window's style mask to the updated style mask. The logic is straightforward and there are no apparent issues.

v3/pkg/application/webview_window.go (1)

260-260: Ensure All References to processKeyBindingOptions Are Updated

The function processKeyBindingOptions has been renamed to windowProcessKeyBindingOptions. Please ensure that all occurrences of the old function name have been updated throughout the codebase to prevent any undefined function errors.

Run the following script to verify no references to the old function name remain:

Verification successful

To gather more information and clarify the situation, I'll run additional scripts to investigate the windowProcessKeyBindingOptions function and the context of other processKeyBindingOptions occurrences.


Renaming of processKeyBindingOptions is Correct and Intentional

The renaming of processKeyBindingOptions to windowProcessKeyBindingOptions in v3/pkg/application/webview_window.go is correct and intentional. This change makes the function specific to WebviewWindow type.

Other similarly named functions in the codebase serve different purposes:

  • processKeyBindingOptionsForPanel in webview_panel_options.go for WebviewPanel type
  • processKeyBindingOptions in application.go for general Window type

These functions should not be renamed as they are intentionally different for type-specific key binding processing.

The original review comment's recommendation to update all occurrences was overly broad. The current implementation maintains clear separation of concerns for different window types.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any occurrences of `processKeyBindingOptions` in Go files.

# Test: Find references to `processKeyBindingOptions`. Expect: No occurrences.
rg --type go 'processKeyBindingOptions'

Length of output: 687


Script:

#!/bin/bash
# Search for windowProcessKeyBindingOptions
echo "Searching for windowProcessKeyBindingOptions:"
rg --type go 'windowProcessKeyBindingOptions'

# Show context for processKeyBindingOptions occurrences
echo -e "\nContext for processKeyBindingOptions occurrences:"
rg --type go -C 5 'processKeyBindingOptions'

Length of output: 3458

v3/pkg/application/webview_window.go Show resolved Hide resolved
Copy link

sonarcloud bot commented Sep 21, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
14.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarCloud

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 2ede8e2 and c142752.

Files selected for processing (9)
  • v3/pkg/application/application.go (12 hunks)
  • v3/pkg/application/webview_panel.go (1 hunks)
  • v3/pkg/application/webview_panel_darwin.go (1 hunks)
  • v3/pkg/application/webview_panel_options.go (1 hunks)
  • v3/pkg/application/webview_responder_darwin.h (1 hunks)
  • v3/pkg/application/webview_responder_darwin.m (1 hunks)
  • v3/pkg/application/webview_window.go (4 hunks)
  • v3/pkg/application/webview_window_bindings_darwin.h (1 hunks)
  • v3/pkg/application/webview_window_bindings_darwin.m (1 hunks)
Files skipped from review as they are similar to previous changes (7)
  • v3/pkg/application/application.go
  • v3/pkg/application/webview_panel.go
  • v3/pkg/application/webview_panel_darwin.go
  • v3/pkg/application/webview_panel_options.go
  • v3/pkg/application/webview_responder_darwin.h
  • v3/pkg/application/webview_responder_darwin.m
  • v3/pkg/application/webview_window_bindings_darwin.h
Additional comments not posted (12)
v3/pkg/application/webview_window_bindings_darwin.m (10)

3-114: LGTM!

The createWindow function correctly handles the creation of both window and panel types based on the provided WindowType parameter. It sets up the window style, delegate, content view, and embeds a properly configured WKWebView. The function also enables drag and drop if requested.


116-157: LGTM!

The printWindowStyle function is a helpful utility for debugging purposes. It retrieves the window style mask and delegate, and then prints the corresponding string representation for each style mask flag. This function does not affect the functionality of the window.


161-167: LGTM!

The setInvisibleTitleBarHeight function correctly sets the invisible title bar height for the window by retrieving the window and its delegate, and then setting the invisibleTitleBarHeight property of the delegate to the provided height value. This function is useful for customizing the appearance of the window's title bar.


170-173: LGTM!

The windowSetTransparent function correctly makes the window backdrop transparent by setting the window's opaque property to NO and its background color to clearColor. This function is useful for creating transparent windows.


189-195: LGTM!

The windowSetSize function correctly sets the size of the window by calculating the content size based on the provided width and height, and then setting the window's content size and frame using the calculated content size. The function also animates the window resizing, providing a smooth visual experience.


198-201: LGTM!

The windowSetAlwaysOnTop function correctly sets the window to be always on top by setting its level to NSFloatingWindowLevel if alwaysOnTop is true, or NSNormalWindowLevel otherwise. This function is useful for controlling the window's behavior in relation to other windows.


203-210: LGTM!

The functions setNormalWindowLevel, setFloatingWindowLevel, setPopUpMenuWindowLevel, setMainMenuWindowLevel, setStatusWindowLevel, setModalPanelWindowLevel, setScreenSaverWindowLevel, and setTornOffMenuWindowLevel correctly set the window level to different predefined levels using the setLevel method. These functions provide a convenient way to control the window's behavior and appearance in relation to other windows.


223-233: LGTM!

The windowSetResizable function correctly sets whether the window is resizable by retrieving the window's current style mask and adding or removing the NSWindowStyleMaskResizable flag based on the resizable parameter. The function then sets the updated style mask on the window, effectively controlling its resizability.


236-243: LGTM!

The windowSetMinSize function correctly sets the minimum size of the window by calculating the content size based on the provided width and height, and then setting the window's minimum content size using the setContentMinSize method. The function also sets the window's minimum size using the setMinSize method, ensuring that the window cannot be resized below the specified dimensions.


246-255: LGTM!

The windowSetMaxSize function correctly sets the maximum size of the window by initializing the maximum size to FLT_MAX for both width and height, and updating the corresponding dimension if the provided width or height is greater than 0. The function then calculates the content size based on the maximum size and sets the window's maximum content size using the setContentMaxSize method. It also sets the window's maximum size using the setMaxSize method, ensuring that the window cannot be resized beyond the specified dimensions.

v3/pkg/application/webview_window.go (2)

Line range hint 221-262: LGTM!

The introduction of the id variable and its usage to set options.Name and initialize the id field is a good optimization that ensures consistency and reduces the chances of errors.

The renaming of processKeyBindingOptions to windowProcessKeyBindingOptions improves clarity and follows a consistent naming convention. The function call within NewWindow has been updated correctly to reflect this change.


1234-1237: Condition update is correct.

The change in the condition from w.impl == nil && !w.isDestroyed() to w.impl == nil || w.isDestroyed() is correct and ensures that the function returns early without handling the key event if either w.impl is nil or the window is destroyed.

This prevents unexpected behavior that could have occurred with the previous condition, where the key event would be handled if the window was destroyed but w.impl was not nil.

Comment on lines +175 to +179
void windowSetInvisibleTitleBar(void* window, unsigned int height) {
NSWindow* nsWindow = ((WebviewWindow*)window).w;
WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate];
delegate.invisibleTitleBarHeight = height;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the duplicate function.

The windowSetInvisibleTitleBar function is a duplicate of the setInvisibleTitleBarHeight function at line range 161-167. To maintain code clarity and avoid confusion, consider removing this duplicate function.

@nixpare
Copy link
Author

nixpare commented Sep 21, 2024

@leaanthony I think I have addressed every issue I could do alone, now the majority of the remaining points are all linked to code that I have neither added nor modified. I hope to have been helpful!

@@ -47,7 +47,12 @@ func NewCutMenuItem() *MenuItem {
SetAccelerator("CmdOrCtrl+x").OnClick(func(ctx *Context) {
currentWindow := globalApplication.CurrentWindow()
if currentWindow != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add cut() to the Window interface. It can be done in a different PR

@leaanthony
Copy link
Member

Regarding the free() comments by rabbit, it's desirable to free the memory in the same function as the allocation. Could we move those free's to the calling method that creates the C String?

@leaanthony
Copy link
Member

Hi @nixpare - Sorry to ask this: could you please fix the current merge conflict with this PR? Thank you 🙏

@leaanthony
Copy link
Member

@nixpare - do you still want this PR merged?

@leaanthony leaanthony added the awaiting feedback More information is required from the requestor label Nov 17, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (11)
v3/pkg/application/webview_panel_options.go (3)

6-7: Fix grammatical error in comment

The comment for the Floating field has a grammatical error.

-	// Floating will make the panel float above other application in every workspace.
+	// Floating will make the panel float above other applications in every workspace.

20-20: Add function documentation

The function lacks documentation explaining its purpose, parameters, and return value.

+// processKeyBindingOptionsForPanel merges window and panel-specific key bindings into a single map.
+// Panel-specific bindings take precedence over window bindings when keys conflict.
+// Parameters:
+//   - keyBindings: Panel-specific key bindings
+//   - windowKeyBindings: Base window key bindings
+// Returns: A merged map of key bindings for the panel
 func processKeyBindingOptionsForPanel(keyBindings map[string]func(panel *WebviewPanel), windowKeyBindings map[string]func(panel *WebviewWindow)) map[string]func(panel *WebviewPanel) {

23-44: Refactor duplicate key binding processing logic

The key binding processing logic is duplicated between window and panel bindings. Consider extracting the common validation and logging into a helper function.

+func processBinding(key string, result map[string]func(panel *WebviewPanel), callback func(panel *WebviewPanel)) {
+	acc, err := parseAccelerator(key)
+	if err != nil {
+		globalApplication.error("Invalid keybinding: %s", err.Error())
+		return
+	}
+	result[acc.String()] = callback
+	globalApplication.debug("Added Keybinding", "accelerator", acc.String())
+}
+
 func processKeyBindingOptionsForPanel(keyBindings map[string]func(panel *WebviewPanel), windowKeyBindings map[string]func(panel *WebviewWindow)) map[string]func(panel *WebviewPanel) {
 	result := make(map[string]func(panel *WebviewPanel))
 
 	for key, callback := range windowKeyBindings {
-		acc, err := parseAccelerator(key)
-		if err != nil {
-			globalApplication.error("Invalid keybinding: %s", err.Error())
-			continue
-		}
-		result[acc.String()] = func(panel *WebviewPanel) {
+		processBinding(key, result, func(panel *WebviewPanel) {
 			callback(panel.WebviewWindow)
-		}
-		globalApplication.debug("Added Keybinding", "accelerator", acc.String())
+		})
 	}
 
 	for key, callback := range keyBindings {
-		// Parse the key to an accelerator
-		acc, err := parseAccelerator(key)
-		if err != nil {
-			globalApplication.error("Invalid keybinding: %s", err.Error())
-			continue
-		}
-		result[acc.String()] = callback
-		globalApplication.debug("Added Keybinding", "accelerator", acc.String())
+		processBinding(key, result, callback)
 	}
 	return result
 }
v3/pkg/application/webview_panel.go (2)

3-7: Add interface documentation.

Please add documentation for the webviewPanelImpl interface describing its purpose, responsibilities, and the relationship with webviewWindowImpl. This will help maintainers understand the interface contract.


9-16: Document thread safety guarantees.

The struct contains a map for key bindings which could be accessed concurrently. While there are locks used in the methods, the struct documentation should explicitly state the thread safety guarantees.

Add documentation like:

+// WebviewPanel represents a panel window with additional floating capabilities.
+// Thread safety: Access to keyBindings is protected by keyBindingsLock.
 type WebviewPanel struct {
v3/pkg/application/application.go (3)

175-188: LGTM! Consider enhancing error handling.

The key binding processing logic is well-structured and thread-safe. However, it might be beneficial to collect and report all invalid key bindings at once rather than logging them individually.

Consider this approach:

 func processKeyBindingOptions(keyBindings map[string]func(window Window)) map[string]func(window Window) {
     result := make(map[string]func(window Window))
+    var invalidBindings []string
     for key, callback := range keyBindings {
         acc, err := parseAccelerator(key)
         if err != nil {
-            globalApplication.error("Invalid keybinding: %s", err.Error())
+            invalidBindings = append(invalidBindings, fmt.Sprintf("%s: %s", key, err.Error()))
             continue
         }
         result[acc.String()] = callback
         globalApplication.debug("Added Keybinding", "accelerator", acc.String())
     }
+    if len(invalidBindings) > 0 {
+        globalApplication.error("Invalid keybindings found:\n%s", strings.Join(invalidBindings, "\n"))
+    }
     return result
 }

559-564: Consider adding documentation for panel behavior differences.

While the implementation is solid, it would be beneficial to add documentation explaining:

  • Platform-specific behavior differences for panels
  • Usage guidelines for choosing between windows and panels
  • Limitations on Windows and Linux platforms

Breaking changes detected in type assertions and method calls

The verification reveals multiple locations where the code explicitly relies on *WebviewWindow type assertions and direct method calls that could break with the interface change:

  • Type assertions found in multiple files:
    • v3/pkg/application/dialogs.go
    • v3/pkg/application/dialogs_linux.go
    • v3/pkg/application/linux_purego.go
    • v3/pkg/application/messageprocessor_dialog.go
    • v3/pkg/application/linux_cgo.go
  • Direct method calls in:
    • v3/pkg/application/roles.go: SetURL method
    • v3/pkg/application/messageprocessor_screens.go: GetScreen method
    • v3/examples/menu/main.go: Resizable and SetResizable methods
🔗 Analysis chain

Line range hint 772-783: Verify impact of return type change.

The change in CurrentWindow()'s return type from *WebviewWindow to Window interface is a breaking change that could affect existing code that expects a concrete WebviewWindow type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find potential type assertions or direct WebviewWindow method calls that might break

# Search for type assertions
rg -A 2 '\.\(\*WebviewWindow\)' 

# Search for direct method calls on CurrentWindow()
rg -A 2 'CurrentWindow\(\)\.' 

Length of output: 3026

v3/pkg/application/linux_cgo.go (1)

713-714: Good memory management, but remove extra blank line

The deferred cleanup is good practice, but there's an unnecessary blank line that should be removed for consistency.

func menuItemSetToolTip(widget pointer, tooltip string) {
    value := C.CString(tooltip)
    defer C.free(unsafe.Pointer(value))
-
    C.gtk_widget_set_tooltip_text(
v3/pkg/application/webview_panel_darwin.go (1)

77-77: Improve error handling in handleKeyEvent

While logging the error is helpful, consider returning the error or handling it appropriately to provide better feedback to the caller.

v3/pkg/application/webview_window_darwin.go (1)

492-495: Consistent Use of defer for Memory Management

In the setup method, the C string cCSS is freed immediately after use instead of using defer. For consistency and to ensure the C string is freed even if an error occurs, consider using defer C.free(...) as in other parts of the code.

Apply the following diff to use defer:

 cCSS := C.CString(options.CSS)
-C.windowInjectCSS(w.nsWindow, cCSS)
-C.free(unsafe.Pointer(cCSS))
+defer C.free(unsafe.Pointer(cCSS))
 C.windowInjectCSS(w.nsWindow, cCSS)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c142752 and ac210d9.

⛔ Files ignored due to path filters (1)
  • v3/go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • v3/examples/window/main.go (8 hunks)
  • v3/go.mod (2 hunks)
  • v3/internal/commands/build-assets.go (1 hunks)
  • v3/internal/commands/tool_package_test.go (3 hunks)
  • v3/pkg/application/application.go (12 hunks)
  • v3/pkg/application/application_options.go (1 hunks)
  • v3/pkg/application/keys_linux.go (1 hunks)
  • v3/pkg/application/linux_cgo.go (11 hunks)
  • v3/pkg/application/messageprocessor.go (1 hunks)
  • v3/pkg/application/screen_darwin.go (1 hunks)
  • v3/pkg/application/webview_panel.go (1 hunks)
  • v3/pkg/application/webview_panel_darwin.go (1 hunks)
  • v3/pkg/application/webview_panel_options.go (1 hunks)
  • v3/pkg/application/webview_window.go (4 hunks)
  • v3/pkg/application/webview_window_darwin.go (7 hunks)
  • v3/pkg/application/window.go (3 hunks)
✅ Files skipped from review due to trivial changes (3)
  • v3/pkg/application/keys_linux.go
  • v3/internal/commands/tool_package_test.go
  • v3/pkg/application/messageprocessor.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • v3/pkg/application/screen_darwin.go
  • v3/pkg/application/webview_window.go
  • v3/examples/window/main.go
🧰 Additional context used
📓 Learnings (3)
v3/pkg/application/webview_panel_options.go (2)
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/examples/keybindings/main.go:16-17
Timestamp: 2024-11-12T12:40:39.204Z
Learning: In the codebase, `application.Options.KeyBindings` uses the `application.Window` type, whereas `application.WebviewWindowOptions.KeyBindings` uses `*application.WebviewWindow`. This is intentional and acceptable.
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/pkg/application/application_options.go:93-93
Timestamp: 2024-11-12T12:40:39.204Z
Learning: The `KeyBindings` property of `application.Option` uses the `application.Window` type, while `application.WebviewWindowOptions` still accepts `*application.WebviewWindow`, and this is intentional.
v3/pkg/application/webview_panel_darwin.go (2)
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/pkg/application/webview_panel_darwin.go:88-89
Timestamp: 2024-11-12T12:40:39.204Z
Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/pkg/application/webview_panel_darwin.go:63-69
Timestamp: 2024-11-12T12:40:39.204Z
Learning: The function `panelNew` is defined in `webview_window_bindings_darwin.h` and is properly included in the code.
v3/pkg/application/application_options.go (2)
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/examples/keybindings/main.go:16-17
Timestamp: 2024-11-12T12:40:39.204Z
Learning: In the codebase, `application.Options.KeyBindings` uses the `application.Window` type, whereas `application.WebviewWindowOptions.KeyBindings` uses `*application.WebviewWindow`. This is intentional and acceptable.
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/pkg/application/application_options.go:93-93
Timestamp: 2024-11-12T12:40:39.204Z
Learning: The `KeyBindings` property of `application.Option` uses the `application.Window` type, while `application.WebviewWindowOptions` still accepts `*application.WebviewWindow`, and this is intentional.
🔇 Additional comments (30)
v3/pkg/application/webview_panel_options.go (2)

3-18: Well-designed struct with clear documentation

The WebviewPanelOptions struct is well-designed with:

  • Clear separation of concerns through embedding
  • Proper documentation explaining merge behavior
  • Type-safe callback signatures

1-46: Consider platform-specific behavior handling

According to the PR description, WebviewPanel should only be fully functional on MacOS, with potential panics on Windows. Consider adding platform-specific checks or documentation to prevent misuse on unsupported platforms.

Let's check if there are any platform-specific guards in related files:

v3/pkg/application/window.go (3)

15-16: Clarify the relationship between Bounds and existing position/size methods

The new Bounds() and SetBounds() methods seem to overlap with existing Position(), Size(), and their variants. Please clarify:

  1. How does Rect differ from using separate position and size?
  2. When should developers use Bounds vs the existing methods?
  3. Is Rect defined in another file? If so, please document the import.

Consider:

  • Adding documentation comments explaining the purpose and usage
  • Including examples of when to use each method
  • Providing migration guidelines for existing implementations

27-27: Document Flash behavior across platforms

The Flash method needs documentation about its platform-specific behavior, especially since:

  • Windows panics with WebviewPanel
  • Linux hasn't been tested
  • It's primarily for MacOS Panel support

Consider:

  • Adding documentation comments explaining platform-specific behavior
  • Including graceful fallbacks for unsupported platforms
  • Providing usage examples in the documentation

Line range hint 15-60: Breaking Changes: Provide migration guide

These interface additions constitute breaking changes that require all implementers to add new methods. Please:

  1. Document the breaking changes in CHANGELOG.md
  2. Provide a migration guide for existing implementations
  3. Consider adding default implementations where possible

Consider:

  • Creating an abstract base implementation with reasonable defaults
  • Providing a migration timeline
  • Adding deprecation notices if any methods are being replaced
v3/pkg/application/webview_panel.go (1)

47-56: Add state transition validation.

The SetFloating method should validate if the state transition is allowed in the current panel lifecycle stage. For example, it might not be valid to change floating state while the panel is being destroyed.

Let's verify the current usage:

v3/go.mod (2)

36-36: Verify MacOS system calls support

The golang.org/x/sys package is now a direct dependency, which aligns with implementing MacOS panel support. However, let's verify that the version v0.27.0 includes all necessary MacOS system calls.

✅ Verification successful

Let me gather more information about the MacOS panel support by searching for panel-related code in the codebase.


Based on the search results, I can now provide a definitive response about the MacOS panel support and the golang.org/x/sys dependency.

The golang.org/x/sys v0.27.0 dependency is appropriate for MacOS panel support

The codebase shows comprehensive MacOS panel implementation that relies on system-level functionality:

  • Native NSPanel integration in v3/pkg/application/webview_panel_darwin.go
  • Window level management through setWindowLevel functions
  • Panel-specific features like floating panels and window stacking

The golang.org/x/sys v0.27.0 package is correctly positioned as a direct dependency since it provides the necessary low-level system interfaces for:

  • Window/panel management
  • System-level event handling
  • OS-specific functionality required by the MacOS panel implementation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check golang.org/x/sys for MacOS panel-related functionality

# Search for MacOS-specific panel/window management code
rg -i "darwin.*panel|darwin.*window" $(fd -t f . $(go env GOPATH)/pkg/mod/golang.org/x/[email protected])

Length of output: 22509


Script:

#!/bin/bash
# Search for panel-related code in the codebase
rg -i "panel|window.*level" --type go

Length of output: 21946


17-17: Clarify the addition of nfpm package

The addition of github.com/goreleaser/nfpm/v2 seems unrelated to the MacOS panel feature. This package is typically used for building Linux packages (deb, rpm). Could you clarify the reason for adding this dependency in this PR?

v3/internal/commands/build-assets.go (2)

130-133: LGTM! The formatting changes improve readability.

The consistent indentation in the WailsConfig struct's Info fields improves code readability while maintaining the same functionality.


130-133: Consider adding panel-specific configuration fields.

Given that this PR introduces MacOS Panel support, consider whether panel-specific configuration fields (e.g., panel behavior, default position) should be added to the WailsConfig struct.

Let's check if any panel-related configuration is defined elsewhere:

If no panel configuration exists yet, consider adding fields like:

  • defaultPanelPosition
  • panelBehavior
  • panelSize
    to support customization of the new panel functionality.
v3/pkg/application/application_options.go (1)

92-95: Well-documented interface change!

The documentation clearly explains the transition to the Window interface and provides good guidance on maintaining type-specific control through window options or type switching.

v3/pkg/application/application.go (3)

554-578: LGTM! Well-structured window management refactoring.

The extraction of common window initialization logic into addNewWindow is a great improvement that:

  • Reduces code duplication
  • Ensures consistent window lifecycle management
  • Maintains thread safety

Line range hint 988-1008: LGTM! Thread-safe key binding implementation.

The key binding implementation is well-designed with:

  • Proper use of RWMutex for concurrent access optimization
  • Clean separation of concerns between processing and management
  • Appropriate goroutine usage for callback execution

721-721: Update error messages to use generic Window type.

The error messages should use the generic "Window" term instead of specific window types to maintain consistency with the updated architecture.

Also applies to: 734-734, 757-757, 1026-1026

v3/pkg/application/linux_cgo.go (6)

485-485: Good memory management practice!

The deferred cleanup of the C string ensures proper memory management and prevents memory leaks.


665-665: Good memory management practice!

The deferred cleanup of the C string ensures proper memory management when setting menu item labels.


1192-1192: Good memory management practice!

The deferred cleanup of the C string ensures proper memory management when setting window titles.


1219-1219: Good memory management practice!

The deferred cleanup of the C string ensures proper memory management when setting window URLs.


1376-1376: Good memory management practice!

The deferred cleanup of the C string ensures proper memory management during window reloads.


1625-1631: Good memory management practice!

The deferred cleanup of C strings is properly placed immediately after allocation, ensuring proper memory management for file chooser dialogs.

v3/pkg/application/webview_panel_darwin.go (3)

31-85: Unit tests are missing for macosWebviewPanel

No unit tests are provided for the new macosWebviewPanel implementation. Adding tests will help ensure that the panel behaves as expected and will prevent future regressions.


51-53: Potential issue with event listener iteration

When iterating over p.parent.eventListeners, consider whether you should be iterating over the values or the keys. Currently, you're iterating over the keys:

for eventId := range p.parent.eventListeners {
    p.on(eventId)
}

If eventListeners is a map and you need both keys and values, consider using:

for eventId, listener := range p.parent.eventListeners {
    // Use eventId and listener as needed
}

83-85: ⚠️ Potential issue

Consider adding a nil check for p.nsPanel before calling C.panelSetFloating

Accessing p.nsPanel without ensuring it's not nil may lead to runtime errors if setFloating is called before the panel is initialized. Adding a nil check can prevent a potential nil pointer dereference.

Apply this diff to add a nil check:

func (p *macosWebviewPanel) setFloating(floating bool) {
+    if p.nsPanel == nil {
+        return
+    }
     C.panelSetFloating(p.nsPanel, C.bool(floating))
}
⛔ Skipped due to learnings
Learnt from: nixpare
PR: wailsapp/wails#3763
File: v3/pkg/application/webview_panel_darwin.go:88-89
Timestamp: 2024-11-12T12:40:39.204Z
Learning: Safety checks for `p.nsPanel` are performed in the `SetFloating` method of `WebviewPanel`, following the `WebviewWindow` and `macosWebviewWindow` implementations and code style.
v3/pkg/application/webview_window_darwin.go (7)

11-12: Consistent Window Creation Function

The windowNew function has been updated to call createWindow with the specified parameters, ensuring consistent window creation. This change enhances maintainability.


246-248: Proper Memory Management of C Strings

C strings are now correctly allocated and freed using C.CString and defer C.free(...) in functions such as execJS, setURL, setTitle, setup, and setHTML. This addresses previous memory leak concerns noted in earlier reviews.

Also applies to: 253-255, 275-275, 454-457, 600-600


399-401: Modularization of Window Setup Logic

The creation and initialization of window properties have been refactored into a new setup method. This improves code organization, readability, and maintainability by encapsulating the setup logic.

Also applies to: 403-530


419-425: Verify Backdrop Configuration Effects

The switch statement for macOptions.Backdrop configures the window's backdrop settings. Ensure that the functions windowSetTransparent, webviewSetTransparent, and windowSetTranslucent are applied correctly and that the visual effects match the intended design on macOS.

Test the backdrop settings by running the application with different Backdrop options and observing the window's appearance to confirm the settings are applied as expected.


492-495: Potential Issues with Shadow and Always-On-Top Settings

When the window is hidden initially and then shown later, the code sets up a callback to adjust the shadow and always-on-top properties upon the WindowDidBecomeKey event. Ensure that this callback is correctly invoked and that the properties are set as intended, even if the window does not become key immediately.

Test this scenario by launching the application with the window hidden (options.Hidden is true) and verify that the shadow and always-on-top settings are correctly applied when the window is shown.

Also applies to: 503-507


515-526: Event Handling for Window Focus and Resize

Event listeners for window focus loss (WindowDidResignKey, WindowDidResignMain) and resize (WindowDidResize) have been added. Confirm that these events are correctly emitted and handled, and that any associated functionality (like UI updates) responds appropriately.


201-201: Confirm Correct Function Name Usage

The function windowIsFullScreen is called to check if the window is in fullscreen mode. Ensure that this function exists in the C bindings and that the naming is consistent across all uses.

Run the following script to check for the correct function name usage:

✅ Verification successful

Function windowIsFullScreen is correctly defined and used

The verification confirms that the function windowIsFullScreen is properly defined in the C bindings file (webview_window_bindings_darwin.h) with the exact same name and case sensitivity as it's being used in the Go code. The function signature matches the expected usage for checking window fullscreen state.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that 'windowIsFullScreen' is consistently used.

# Test: Search for both 'windowIsFullScreen' and 'windowIsFullscreen' in the C binding files.
# Expect: Only 'windowIsFullScreen' should be present.
rg 'windowIsFullScreen|windowIsFullscreen' v3/pkg/application/webview_window_bindings_darwin.h

Length of output: 136

RegisterContextMenu(name string, menu *Menu)
RelativePosition() (int, int)
Reload()
Resizable() bool
Restore()
Run()
SetEnabled(enabled bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the gathered information, I can now generate the final response:

Document SetEnabled impact and focus management

The SetEnabled method requires documentation as it has significant implications:

  • It's implemented across multiple platforms (Darwin/macOS, Windows, Linux)
  • It interacts with the window's focus management system
  • It affects the window's interaction state

Key areas to document:

  • The method directly controls the window's enabled state through platform-specific implementations
  • It has a relationship with focus management (Focus(), IsFocused())
  • It should address high-contrast and accessibility modes supported by the application
🔗 Analysis chain

Document SetEnabled impact and accessibility considerations

The SetEnabled method needs documentation about:

  • Impact on window interaction
  • Relationship with focus management
  • Accessibility implications

Consider:

  • Adding documentation comments explaining the enabled/disabled states
  • Including accessibility guidelines
  • Providing examples of proper state management

Let me gather more context about the window implementation and any existing documentation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for SetEnabled implementation and usage
rg -g '**/*window*.go' 'SetEnabled.*bool' -A 5

Length of output: 1221


Script:

#!/bin/bash
# Look for any existing documentation or comments about window enabling/disabling
rg -g '**/*window*.go' -B 5 'SetEnabled'

# Check for any accessibility-related code or comments
rg -g '**/*window*.go' -i 'accessibility|aria|a11y'

# Look for focus management related code
rg -g '**/*window*.go' 'focus|Focus'

Length of output: 7724

@@ -47,12 +50,14 @@ type Window interface {
OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func()
OpenContextMenu(data *ContextMenuData)
Position() (int, int)
Print() error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the analysis of the implementation across different platforms, I can now provide a final response:

Document platform-specific Print behavior and error cases

The Print() method implementation varies by platform:

  • Windows/Linux: Invokes browser's window.print() JavaScript function
  • macOS: Uses native windowPrint Cocoa API

Error cases:

  • Returns nil if window is destroyed
  • Delegates actual printing errors to the platform's native print dialog
🔗 Analysis chain

Document Print error cases and platform support

The Print() method returns an error but lacks documentation about:

  • Possible error cases
  • Platform-specific behavior
  • Required window state for printing

Consider:

  • Adding documentation comments explaining error conditions
  • Including platform-specific limitations
  • Providing examples of proper error handling

Let me check the implementation in more detail to understand the error handling and platform-specific behavior.


Let me check one more thing to see if there are any related tests or additional documentation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Print implementation and error handling
rg -g '**/*window*.go' 'Print\(\).*error' -A 5

Length of output: 718


Script:

#!/bin/bash
# Search for print implementation in platform-specific files
rg -g '**/*.go' 'print\(\).*error' -A 5

# Look for any documentation or comments about printing
rg -g '**/*.go' -B 3 'print\(\).*error'

Length of output: 2766


Script:

#!/bin/bash
# Look for test files and documentation
rg -g '**/*window*.go' -g '**/*print*.go' -g '**/README*' -i 'print.*window' -B 2 -A 2

# Check if there are any C header files for macOS implementation
fd -g '*.h' -x cat {} \; 

Length of output: 38101

Comment on lines +67 to +90
func (p *WebviewPanel) processKeyBinding(acceleratorString string) bool {
// Check menu bindings
if p.menuBindings != nil {
p.menuBindingsLock.RLock()
defer p.menuBindingsLock.RUnlock()
if menuItem := p.menuBindings[acceleratorString]; menuItem != nil {
menuItem.handleClick()
return true
}
}

// Check key bindings
if p.keyBindings != nil {
p.keyBindingsLock.RLock()
defer p.keyBindingsLock.RUnlock()
if callback := p.keyBindings[acceleratorString]; callback != nil {
// Execute callback
go callback(p)
return true
}
}

return globalApplication.processKeyBinding(acceleratorString, p.WebviewWindow)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve goroutine management and error handling consistency.

The key event handling has several areas for improvement:

  1. Launching unbounded goroutines for callbacks could lead to resource exhaustion
  2. Inconsistent error handling between menu and key callbacks
  3. No timeout handling for callbacks

Consider using a worker pool for callbacks and consistent error handling:

 func (p *WebviewPanel) processKeyBinding(acceleratorString string) bool {
     // Check menu bindings
     if p.menuBindings != nil {
         p.menuBindingsLock.RLock()
         defer p.menuBindingsLock.RUnlock()
         if menuItem := p.menuBindings[acceleratorString]; menuItem != nil {
-            menuItem.handleClick()
+            if err := menuItem.handleClick(); err != nil {
+                log.Printf("menu binding handler failed: %v", err)
+            }
             return true
         }
     }
 
     // Check key bindings
     if p.keyBindings != nil {
         p.keyBindingsLock.RLock()
         defer p.keyBindingsLock.RUnlock()
         if callback := p.keyBindings[acceleratorString]; callback != nil {
-            // Execute callback
-            go callback(p)
+            // Execute callback with timeout
+            select {
+            case p.callbackWorkerPool <- struct{}{}:
+                go func() {
+                    defer func() { <-p.callbackWorkerPool }()
+                    callback(p)
+                }()
+            case <-time.After(callbackTimeout):
+                log.Printf("callback worker pool full, dropping key event: %s", acceleratorString)
+            }
             return true
         }
     }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +36 to +46
func (p *WebviewPanel) Run() {
if p.impl != nil {
return
}

p.impl = newPanelImpl(p)
p.WebviewWindow.impl = p.impl.getWebviewWindowImpl()

InvokeSync(p.impl.run)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and documentation.

The Run method needs documentation explaining when it should be called in the panel lifecycle. Additionally, consider handling errors from impl.run.

Consider:

+// Run initializes and starts the panel. It should be called after all panel
+// configurations are set. This method is idempotent - subsequent calls will
+// have no effect.
 func (p *WebviewPanel) Run() {
     if p.impl != nil {
         return
     }
 
     p.impl = newPanelImpl(p)
     p.WebviewWindow.impl = p.impl.getWebviewWindowImpl()
 
-    InvokeSync(p.impl.run)
+    var err error
+    InvokeSync(func() {
+        err = p.impl.run()
+    })
+    if err != nil {
+        log.Printf("failed to run panel: %v", err)
+    }
 }

Committable suggestion skipped: line range outside the PR's diff.

v3/pkg/application/webview_panel.go Show resolved Hide resolved
Comment on lines +1 to +90
package application

type webviewPanelImpl interface {
webviewWindowImpl
getWebviewWindowImpl() webviewWindowImpl
setFloating(floating bool)
}

type WebviewPanel struct {
*WebviewWindow

options WebviewPanelOptions
impl webviewPanelImpl
// keyBindings holds the keybindings for the panel
keyBindings map[string]func(*WebviewPanel)
}

// NewPanel creates a new panel with the given options
func NewPanel(options WebviewPanelOptions) *WebviewPanel {
window := NewWindow(options.WebviewWindowOptions)
options.WebviewWindowOptions = window.options

result := &WebviewPanel{
WebviewWindow: window,
options: options,
}

// Process keybindings specific to the WebviewPanel
if result.options.KeyBindings != nil || result.options.WebviewWindowOptions.KeyBindings != nil {
result.keyBindings = processKeyBindingOptionsForPanel(result.options.KeyBindings, result.options.WebviewWindowOptions.KeyBindings)
}

return result
}

func (p *WebviewPanel) Run() {
if p.impl != nil {
return
}

p.impl = newPanelImpl(p)
p.WebviewWindow.impl = p.impl.getWebviewWindowImpl()

InvokeSync(p.impl.run)
}

// SetFloating makes the panel float above other application in every workspace.
func (p *WebviewPanel) SetFloating(b bool) Window {
p.options.Floating = b
if p.impl != nil {
InvokeSync(func() {
p.impl.setFloating(b)
})
}
return p
}

func (p *WebviewPanel) HandleKeyEvent(acceleratorString string) {
if p.impl == nil || p.isDestroyed() {
return
}
InvokeSync(func() {
p.impl.handleKeyEvent(acceleratorString)
})
}

func (p *WebviewPanel) processKeyBinding(acceleratorString string) bool {
// Check menu bindings
if p.menuBindings != nil {
p.menuBindingsLock.RLock()
defer p.menuBindingsLock.RUnlock()
if menuItem := p.menuBindings[acceleratorString]; menuItem != nil {
menuItem.handleClick()
return true
}
}

// Check key bindings
if p.keyBindings != nil {
p.keyBindingsLock.RLock()
defer p.keyBindingsLock.RUnlock()
if callback := p.keyBindings[acceleratorString]; callback != nil {
// Execute callback
go callback(p)
return true
}
}

return globalApplication.processKeyBinding(acceleratorString, p.WebviewWindow)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add platform-specific handling.

According to the PR objectives, this implementation is specifically for MacOS and currently panics on Windows. The code should include proper platform-specific handling to prevent panics and provide meaningful error messages on unsupported platforms.

Consider:

  1. Add build tags to restrict this file to MacOS
  2. Provide stub implementations for other platforms with appropriate error messages
  3. Add platform-specific documentation

Example:

+// +build darwin
+
 package application
+
+// WebviewPanel is currently only supported on MacOS.
+// Attempting to use this on other platforms will result in a runtime error.

Committable suggestion skipped: line range outside the PR's diff.

// interface, so if you need more granular control over the window (e.g. [*WebviewWindow]), it's possible
// to declare the key binding directly in the specific window options or switch over
// the types that implement the [Window] interface
KeyBindings map[string]func(window Window)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the search results, I can now generate the final response:

Breaking change verified: KeyBindings type change requires updates

The change from *WebviewWindow to Window interface in KeyBindings field is a breaking change that affects existing implementations. Found several examples that need updates:

  • examples/systray/main.go: Uses map[string]func(window *application.WebviewWindow)
  • examples/keybindings/main.go: Two instances using map[string]func(window *application.WebviewWindow)
  • examples/events-bug/main.go: Two instances using map[string]func(window *application.WebviewWindow)

These examples need to be updated to use the new Window interface type in their key binding function signatures.

🔗 Analysis chain

Breaking change: KeyBindings now accepts Window interface

The change from *WebviewWindow to Window interface makes the API more flexible and enables support for both WebviewWindow and WebviewPanel. This is a breaking change that requires updates to existing key binding implementations.

Let's verify the impact on existing key binding implementations:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check existing key binding implementations that might need updates

# Search for key binding function declarations
echo "Checking key binding function declarations..."
rg --type go -A 2 "func.*\(.*WebviewWindow\)" 

# Search for direct KeyBindings usage
echo "Checking KeyBindings field usage..."
rg --type go -A 2 "KeyBindings.*map\[string\]func.*WebviewWindow"

Length of output: 100532

Comment on lines +1488 to +1490
var debounceTimer *time.Timer
var isDebouncing bool = false

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider per-window debounce implementation

The current implementation uses global variables for debouncing, which could lead to race conditions in multi-window scenarios. Consider moving the debounce timer and state into the window struct for better isolation and thread safety.

-var debounceTimer *time.Timer
-var isDebouncing bool = false

type linuxWebviewWindow struct {
    // ... existing fields ...
+   debounceTimer *time.Timer
+   isDebouncing bool
}

Committable suggestion skipped: line range outside the PR's diff.

@leaanthony
Copy link
Member

@nixpare - I had a little dig in to this today as there's been a few other things that have been merged in and I found a number of issues which I'm wondering if they were part of a bad merge. The Bounds and SetBounds were not part of Window and the Panel appears to operate just like a normal WebView window.

If you could possibly have a little look over and see anything that appears out of the ordinary, then hopefully we can resolve it and get it merged. I've added a new menu option in the window example but I think a dedicated window panel example would be pretty cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting feedback More information is required from the requestor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants