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

Double click on ToolBarIconButton maximises window #308

Open
cbenhagen opened this issue Sep 25, 2022 · 15 comments
Open

Double click on ToolBarIconButton maximises window #308

cbenhagen opened this issue Sep 25, 2022 · 15 comments
Labels
bug Something isn't working

Comments

@cbenhagen
Copy link
Contributor

Description

Double click on ToolBarIconButton maximises window

Steps To Reproduce

  1. Open example app
  2. Double click on any ToolBarIconButton
  3. Window maximises

Expected behavior

ToolBarIconButton should not pass mouse events to window

Screen recording

toolbar.mov
@cbenhagen cbenhagen added the bug Something isn't working label Sep 25, 2022
@GroovinChip
Copy link
Collaborator

@whiplashoo any thoughts on how we can account for this? Perhaps an AbsorbPointer or IgnorePointer somewhere?

@whiplashoo
Copy link
Collaborator

The issue is that the double click behavior is handled by the native NSToolbar. The double click event doesn't even reach the toolbar widget and its children, so the AbsorbPointer, IgnorePointer, or using a GestureDetector's onDoubleTap don't seem to work.

@GroovinChip
Copy link
Collaborator

I wonder if there's a way to block double-click events for widgets

@cbenhagen
Copy link
Contributor Author

@GroovinChip / @whiplashoo any ideas how we could solve this?

@GroovinChip
Copy link
Collaborator

Unfortunately, I do not. Hopefully @whiplashoo might.

@cbenhagen
Copy link
Contributor Author

cbenhagen commented Feb 19, 2023

I guess if we don't want to draw the whole window decorations ourselves, using NSToolbar seems to be unavoidable. The following example creates a transparent NSToolbarItem which blocks hits to the toolbar and forwards mouse down and up events to Flutter.

This is just a proof of concept. We might want to use a MethodChannel to communicate the current width it has to block based on the Flutter widgets present in the toolbar widgets defined in Flutter.

class BlockingToolbar: NSToolbar, NSToolbarDelegate {
  let flutterView: BlurryContainerViewController
  
  init(flutterView: BlurryContainerViewController) {
      self.flutterView = flutterView
      super.init(identifier: "BlockingToolbar")
  }

  func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    [.flexibleSpace, NSToolbarItem.Identifier("BlockingItem")]
  }

  func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    toolbarDefaultItemIdentifiers(toolbar)
  }

  func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
    if itemIdentifier == NSToolbarItem.Identifier("BlockingItem") {
      let item = NSToolbarItem(itemIdentifier: itemIdentifier)
      let myView = ForwardingView()
      myView.flutterView = flutterView
      item.view = myView
      return item
    }
    return nil
  }
}


class ForwardingView: NSView {
  var flutterView: BlurryContainerViewController?

  override var intrinsicContentSize: NSSize {
    NSSize(width: 50, height: 50)
  }

  override func mouseDown(with event: NSEvent) {
    flutterView!.flutterViewController.mouseDown(with: event)
  }

  override func mouseUp(with event: NSEvent) {
    flutterView!.flutterViewController.mouseUp(with: event)
  }
}

Used like this in MainFlutterWindow:

if #available(macOS 10.13, *) {
  let customToolbar = BlockingToolbar(flutterView: blurryContainerViewController)
  customToolbar.showsBaselineSeparator = false
  customToolbar.delegate = customToolbar
  self.toolbar = customToolbar
}

@whiplashoo / @GroovinChip what do you think?

@GroovinChip
Copy link
Collaborator

Hard to say without trying it out, which I can't because I'm AFK. Can you post a gif of this in action?

@cbenhagen
Copy link
Contributor Author

cbenhagen commented Feb 19, 2023

Sure, here you go:

Screen.Recording.2023-02-19.at.22.30.26.mov

@GroovinChip
Copy link
Collaborator

Looks great, thank you!

I agree with you that keeping NSToolbar seems unavoidable. I like your solution a lot, and I also agree that utilizing a MethodChannel to ensure the blocking dimensions are correct is a good idea. Do you intend to continue your experimentation in that direction?

@cbenhagen
Copy link
Contributor Author

I don't have much time for more than this quick example in the next month. If you like it please feel free to build on this. Otherwise I might get back to this issue later.

@GroovinChip
Copy link
Collaborator

Fair enough. I'll see if I can knock something together.

@cbenhagen
Copy link
Contributor Author

A variation of the code above could be to have a single NSToolbarItem taking the maximum size and thus blocking double click to zoom on the whole toolbar. This way we don't have to scale it through a MethodChannel. The functionality can then be added back by listening on double clicks on the flutter side and zooming through a plugin like https://github.com/leanflutter/window_manager. You just have to take care that buttons in the toolbar don't wait for the the potential double click. Otherwise the interface will feel slow.

Code above coule be changed like this:

class BlockingToolbar: NSToolbar, NSToolbarDelegate {
  let flutterView: BlurryContainerViewController
  
  init(flutterView: BlurryContainerViewController) {
      self.flutterView = flutterView
      super.init(identifier: "BlockingToolbar")
  }

  func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    [.flexibleSpace, NSToolbarItem.Identifier("BlockingItem")]
  }

  func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    toolbarDefaultItemIdentifiers(toolbar)
  }

  func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
    if itemIdentifier == NSToolbarItem.Identifier("BlockingItem") {
      let item = NSToolbarItem(itemIdentifier: itemIdentifier)
      let view = ForwardingView()
      view.flutterView = flutterView
      view.widthAnchor.constraint(lessThanOrEqualToConstant: 100000).isActive = true
      view.widthAnchor.constraint(greaterThanOrEqualToConstant: 1).isActive = true
      item.view = view
      return item
    }
    return nil
  }
}


class ForwardingView: NSView {
  var flutterView: BlurryContainerViewController?

  override func mouseDown(with event: NSEvent) {
    flutterView!.flutterViewController.mouseDown(with: event)
  }

  override func mouseUp(with event: NSEvent) {
    flutterView!.flutterViewController.mouseUp(with: event)
  }
}

Used like this in MainFlutterWindow:

if #available(macOS 10.13, *) {
  let customToolbar = BlockingToolbar(flutterView: blurryContainerViewController)
  customToolbar.showsBaselineSeparator = false
  customToolbar.delegate = customToolbar
  self.toolbar = customToolbar
}

@GroovinChip
Copy link
Collaborator

@cbenhagen I am reluctant to use external plugins for features as it increases our dependency on others, which reduces our ability to iterate on our own timetable. window_manager is a great plugin, but I'd prefer to use solutions that we control.

@cbenhagen
Copy link
Contributor Author

I understand. Nothing is holding us back to just implement the zoom part on our own.

@cbenhagen
Copy link
Contributor Author

I added a PR to macos_window_utils using the BlockingToolbar described above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants