Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 1.5.0
## 1.6.0

- Add support for a blocking toolbar, that is, a special kind of toolbar that blocks double clicks from the user and allows for UI elements to be placed inside a blocked area (thanks to @cbenhagen and @Andre-lbc).

## 1.5.0
- Add the following window methods:
- `preventWindowClosure`
- `allowWindowClosure`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import 'dart:ui';

import 'package:example/main_area/window_manipulator_demo/command_list_provider/command_list_provider_constants.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:macos_window_utils/macos/ns_window_button_type.dart';
import 'package:macos_window_utils/macos/ns_window_level.dart';
import 'package:macos_window_utils/macos/ns_window_style_mask.dart';
import 'package:macos_window_utils/macos_window_utils.dart';
import 'package:macos_window_utils/toolbars/toolbars.dart';

import '../command_list/command.dart';

Expand Down Expand Up @@ -202,6 +204,35 @@ class CommandListProvider {
name: 'addToolbar()',
function: () => WindowManipulator.addToolbar(),
),
Command(
name: 'addToolbar(toolbar: const BlockingToolbar())',
description: 'Adds a blocking toolbar to the window.\n\nBlocking '
'toolbars contain an area that stops double clicks from zooming the'
'window, thus allowing for the placement of buttons that can be '
'clicked repeatedly.\n\nSetting the `blockingAreaDebugColor` to an '
'easily visible color can be useful for debugging purposes:\n\n'
'![image](https://github.com/user-attachments/assets/984c4dc7-f3ea-4b38-ba65-9e611982d32c)'
'You may wish to hide the native title to extend the blocking '
'area:\n\n'
'![image](https://github.com/user-attachments/assets/62e16d4a-1e4d-4c4d-9f1b-f731d08e0b1c)',
function: () =>
WindowManipulator.addToolbar(toolbar: const BlockingToolbar()),
),
Command(
name:
'addToolbar(toolbar: const BlockingToolbar(blockingAreaDebugColor: Colors.red))',
description: 'Adds a blocking toolbar to the window.\n\nBlocking '
'toolbars contain an area that stops double clicks from zooming the'
'window, thus allowing for the placement of buttons that can be '
'clicked repeatedly.\n\nSetting the `blockingAreaDebugColor` to an '
'easily visible color can be useful for debugging purposes:\n\n'
'![image](https://github.com/user-attachments/assets/984c4dc7-f3ea-4b38-ba65-9e611982d32c)'
'You may wish to hide the native title to extend the blocking '
'area:\n\n'
'![image](https://github.com/user-attachments/assets/62e16d4a-1e4d-4c4d-9f1b-f731d08e0b1c)',
function: () => WindowManipulator.addToolbar(
toolbar: const BlockingToolbar(blockingAreaDebugColor: Colors.red)),
),
Command(
name: 'removeToolbar()',
function: () => WindowManipulator.removeToolbar(),
Expand Down
2 changes: 1 addition & 1 deletion example/macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 500e4707112a5f11963bc198135953cdebb6d50c

COCOAPODS: 1.13.0
COCOAPODS: 1.15.2
2 changes: 1 addition & 1 deletion example/macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
43 changes: 43 additions & 0 deletions lib/toolbars/toolbars.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'dart:ui';

abstract class Toolbar {
const Toolbar();

String getName();

Map<String, String> getArguments();
}

class DefaultToolbar extends Toolbar {
const DefaultToolbar();

@override
String getName() {
return "DefaultToolbar";
}

@override
Map<String, String> getArguments() {
return {};
}
}

class BlockingToolbar extends Toolbar {
final Color? blockingAreaDebugColor;

const BlockingToolbar({this.blockingAreaDebugColor});

@override
String getName() {
return "BlockingToolbar";
}

@override
Map<String, String> getArguments() {
return {
"blockingAreaDebugColor": blockingAreaDebugColor == null
? ""
: "${blockingAreaDebugColor!.red},${blockingAreaDebugColor!.green},${blockingAreaDebugColor!.blue},${blockingAreaDebugColor!.alpha}",
};
}
}
35 changes: 33 additions & 2 deletions lib/window_manipulator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:macos_window_utils/macos/visual_effect_view_properties.dart';
import 'package:macos_window_utils/macos/ns_visual_effect_view_material.dart';

import 'ns_window_delegate_handler/ns_window_delegate_handler.dart';
import 'toolbars/toolbars.dart';

/// Class that provides methods to manipulate the application's window.
class WindowManipulator {
Expand Down Expand Up @@ -370,9 +371,39 @@ class WindowManipulator {
}

/// Adds a toolbar to the window.
static Future<void> addToolbar() async {
///
/// By default, the added toolbar is a [DefaultToolbar].
///
/// A [BlockingToolbar] can be added like this:
///
/// ```dart
/// WindowManipulator.addToolbar(
/// toolbar: const BlockingToolbar(blockingAreaDebugColor: Colors.red)),
/// );
/// ```
///
/// Blocking toolbars contain an area that stops double clicks from zooming the
/// window, thus allowing for the placement of buttons that can be clicked
/// repeatedly.
///
/// Setting the `blockingAreaDebugColor` to an easily visible color can be
/// useful for debugging purposes:
///
/// ![image](https://github.com/user-attachments/assets/984c4dc7-f3ea-4b38-ba65-9e611982d32c)
///
/// You may wish to hide the native title to extend the blocking area:
///
/// ![image](https://github.com/user-attachments/assets/62e16d4a-1e4d-4c4d-9f1b-f731d08e0b1c)
static Future<void> addToolbar(
{Toolbar toolbar = const DefaultToolbar()}) async {
await _completer.future;
await _windowManipulatorMethodChannel.invokeMethod('addToolbar');
await _windowManipulatorMethodChannel.invokeMethod(
'addToolbar',
{
'toolbarName': toolbar.getName(),
'toolbarArguments': toolbar.getArguments(),
},
);
}

/// Removes the window's toolbar.
Expand Down
59 changes: 59 additions & 0 deletions macos/Classes/BlockingToolbar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// BlockingToolbar.swift
// macos_window_utils
//
// Created by Ben Hagen on 12.11.2023.
//

import Foundation
import FlutterMacOS

class BlockingToolbar: NSToolbar, NSToolbarDelegate {
let flutterView: FlutterViewController

let blockingAreaDebugColor: NSColor?

init(flutterView: FlutterViewController, blockingAreaDebugColor: NSColor?) {
self.flutterView = flutterView
self.blockingAreaDebugColor = blockingAreaDebugColor
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
if (blockingAreaDebugColor != nil) {
view.wantsLayer = true
view.layer?.backgroundColor = blockingAreaDebugColor!.cgColor
}
return item
}
return nil
}
}


class ForwardingView: NSView {
var flutterView: FlutterViewController?

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

override func mouseUp(with event: NSEvent) {
flutterView!.mouseUp(with: event)
}
}
4 changes: 3 additions & 1 deletion macos/Classes/MacOSWindowUtilsPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,9 @@ public class MacOSWindowUtilsPlugin: NSObject, FlutterPlugin {
break

case "addToolbar":
MainFlutterWindowManipulator.addToolbar()
let toolbarName = args["toolbarName"] as! String
let toolbarArguments = args["toolbarArguments"] as! [String: String]
MainFlutterWindowManipulator.addToolbar(toolbarName: toolbarName, toolbarArguments: toolbarArguments)

result(true)
break
Expand Down
18 changes: 16 additions & 2 deletions macos/Classes/MainFlutterWindowManipulator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -396,15 +396,29 @@ public class MainFlutterWindowManipulator {
macOSWindowUtilsViewController.removeVisualEffectSubview(subviewId)
}

public static func addToolbar() {
public static func addToolbar(toolbarName: String, toolbarArguments: [String: String]) {
if (self.mainFlutterWindow == nil) {
start(mainFlutterWindow: nil)
}

if #available(macOS 10.13, *) {
switch (toolbarName) {
case "DefaultToolbar":
let newToolbar = NSToolbar()

self.mainFlutterWindow!.toolbar = newToolbar

case "BlockingToolbar":
let blockingAreaDebugColor = NSColor.colorFromRGBAString(toolbarArguments["blockingAreaDebugColor"]!)

let customToolbar = BlockingToolbar(flutterView: (self.mainFlutterWindow?.contentViewController as! MacOSWindowUtilsViewController).flutterViewController, blockingAreaDebugColor: blockingAreaDebugColor)
customToolbar.showsBaselineSeparator = false
customToolbar.delegate = customToolbar
self.mainFlutterWindow!.toolbar = customToolbar

default:
print("Unknown toolbar name: \(toolbarName)")
}
}
}

Expand Down
30 changes: 30 additions & 0 deletions macos/Classes/colorFromRGBAString.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// colorFromRGBAString.swift
// macos_window_utils
//
// Created by Adrian Samoticha on 31.10.24.
//

import Foundation

import Cocoa

extension NSColor {
// Function to convert an RGBA string like "255,0,0,128" into NSColor
class func colorFromRGBAString(_ rgbaString: String) -> NSColor? {
let components = rgbaString.split(separator: ",").map { String($0) }

guard components.count == 4,
let red = Double(components[0]),
let green = Double(components[1]),
let blue = Double(components[2]),
let alpha = Double(components[3]) else {
return nil // Return nil if input is invalid
}

return NSColor(red: CGFloat(red / 255.0),
green: CGFloat(green / 255.0),
blue: CGFloat(blue / 255.0),
alpha: CGFloat(alpha / 255.0))
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: macos_window_utils
description:
macos_window_utils is a Flutter package that provides a set of methods for
modifying the NSWindow of a Flutter application on macOS.
version: 1.5.0
version: 1.6.0
repository: https://github.com/Adrian-Samoticha/macos_window_utils.dart

environment:
Expand Down