Skip to content

Commit

Permalink
chore(plugin)!: delegate setting the icon image to new `TrayIconImage…
Browse files Browse the repository at this point in the history
…Delegate`

Users can now subclass the delegate to add their own sources (that are compatible with the available methods in the windows plugin).

Users can also store the delegate class and treat it as an abstract image.

BREAKING CHANGE: `image`, `picture` and `freeRessources` have been removed. Users have to pass a `ByteBuffer` now instead.
  • Loading branch information
benthillerkus committed Mar 31, 2022
1 parent 9807fe6 commit dd7690e
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 35 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"HWND",
"LPARAM",
"LRESULT",
"LTWH",
"scrollbars",
"ulid",
"Ulid"
Expand Down
5 changes: 2 additions & 3 deletions example/add_many/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ class _MyAppState extends State<MyApp> {
setState(() {});

icon.setIcon(
image: img,
// picture: picture,
pixels: (await img?.toByteData(format: ui.ImageByteFormat.rawRgba))
?.buffer,
// asset: "assets/flutter.ico",
freeResources: false,
winIcon: WinIcon.shield);
icon.show();
}
Expand Down
63 changes: 63 additions & 0 deletions lib/src/image.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import 'dart:io';
import 'dart:typed_data';

import 'package:betrayal/src/plugin.dart';
import 'package:betrayal/src/win_icon.dart';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;

/// A helper that sets the image of a [TrayIcon].
@immutable
class TrayIconImageDelegate {
/// A [TrayIconImageDelegate] that loads an image from a `.ico` file.
///
/// If both [uri] and [path] are given, [uri] takes precedence.
/// [path] needs to use Windows style `\\` notation.
TrayIconImageDelegate.fromPath({Uri? uri, String? path}) {
if (uri == null && path == null) {
throw ArgumentError("Either uri or path must be specified.");
}

if (uri != null) {
if (uri.scheme != 'file') {
throw ArgumentError("Only file:// URIs are supported.");
}

path = uri.toFilePath(windows: true);
}

// path.endsWith(".ico");

setIcon = (id, plugin) => plugin.setIconFromPath(id, path!);
}

/// A [TrayIconImageDelegate] that loads a `.ico` file from the Flutter assets directory.
TrayIconImageDelegate.fromAsset(String asset) {
final path = p.joinAll(
[p.dirname(Platform.resolvedExecutable), 'data/flutter_assets', asset]);

setIcon = (id, plugin) => plugin.setIconFromPath(id, path);
}

/// A [TrayIconImageDelegate] that sets an image from the default Windows icons.
TrayIconImageDelegate.fromWinIcon(WinIcon winIcon) {
setIcon = (id, plugin) => plugin.setIconAsWinIcon(id, winIcon.code);
}

/// A [TrayIconImageDelegate] that sets an image from a buffer.
///
/// The buffer is expected to be in the format of a 32x32 RGBA image.
TrayIconImageDelegate.fromBytes(ByteBuffer pixels) {
if (pixels.lengthInBytes != 32 * 32 * 4) {
throw ArgumentError(
"The buffer must be 32x32 pixels and 4 bytes per pixel.");
}
setIcon = (id, plugin) =>
plugin.setIconFromPixels(id, 32, 32, pixels.asInt32List());
}

/// The call into the plugin to set the image for a [TrayIcon] with the given [id].
///
/// This is not part of the public API and usage is discouraged.
late final Future<void> Function(Id id, BetrayalPlugin plugin) setIcon;
}
56 changes: 24 additions & 32 deletions lib/src/imperative.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:ui';
import 'dart:typed_data';

import 'package:betrayal/src/image.dart';
import 'package:betrayal/src/plugin.dart';
import 'package:betrayal/src/data.dart';
import 'package:betrayal/src/win_icon.dart';
import 'package:path/path.dart' as p;

class TrayIcon {
static final BetrayalPlugin _plugin = BetrayalPlugin();
Expand Down Expand Up @@ -80,41 +79,34 @@ class TrayIcon {
await _plugin.setTooltip(_id, tooltip);
}

void setIcon(
{Picture? picture,
Image? image,
Uri? path,
String? asset,
WinIcon winIcon = WinIcon.question,
bool freeResources = false}) async {
/// Sets the image on this icon.
///
/// If multiple arguments are passed, they are resolved in this order:
/// 1. [imageDelegate]
/// 2. [pixels]
/// 3. [path]
/// 4. [asset]
/// 5. [winIcon]
void setIcon({
TrayIconImageDelegate? imageDelegate,
Uri? path,
ByteBuffer? pixels,
String? asset,
WinIcon winIcon = WinIcon.question,
}) async {
_ensureIsActive();
await _makeRealIfNeeded();

if (picture != null) {
if (freeResources) image?.dispose();
image = await picture.toImage(32, 32);
if (freeResources) picture.dispose();
}

if (image != null) {
final bytes = (await image.toByteData(format: ImageByteFormat.rawRgba))!
.buffer
.asInt32List();

await _plugin.setIconFromPixels(_id, image.width, image.height, bytes);

if (freeResources) image.dispose();
if (imageDelegate != null) {
} else if (pixels != null) {
imageDelegate = TrayIconImageDelegate.fromBytes(pixels);
} else if (asset != null) {
var res = p.joinAll([
p.dirname(Platform.resolvedExecutable),
'data/flutter_assets',
asset
]);
await _plugin.setIconFromPath(_id, res);
imageDelegate = TrayIconImageDelegate.fromAsset(asset);
} else if (path != null) {
await _plugin.setIconFromPath(_id, path.toFilePath(windows: true));
imageDelegate = TrayIconImageDelegate.fromPath(uri: path);
} else {
await _plugin.setIconAsWinIcon(_id, winIcon.code);
imageDelegate = TrayIconImageDelegate.fromWinIcon(winIcon);
}
await imageDelegate.setIcon(_id, _plugin);
}
}

0 comments on commit dd7690e

Please sign in to comment.