Skip to content

feat(macos): Add fontFamily support for System Tray title#77

Open
lucaslopes wants to merge 1 commit intoantler119:mainfrom
lucaslopes:feature/macos-font
Open

feat(macos): Add fontFamily support for System Tray title#77
lucaslopes wants to merge 1 commit intoantler119:mainfrom
lucaslopes:feature/macos-font

Conversation

@lucaslopes
Copy link
Copy Markdown

Custom Font

Problem

The macOS tray title currently enforces the standard variable-width system font. This causes visual jitter when displaying dynamic data (like clocks, countdowns, or network speeds) because the width of the text changes constantly.

Solution

This PR adds an optional fontFamily parameter to initSystemTray and setSystemTrayInfo.

  • Backwards Compatible: If fontFamily is omitted, it behaves exactly as before (standard system font).
  • Custom Fonts: Users can pass a font name (e.g., "Menlo").
  • Monospaced Helper: Users can pass "system_monospaced" to automatically use NSFont.monospacedSystemFont (SF Mono) on macOS 10.15+, ensuring perfect alignment for tabular data.

Overview

The system tray plugin supports custom fonts for the tray title, allowing you to:

  • Use system monospaced fonts for clock displays
  • Use custom downloaded fonts (monospaced or proportional)
  • Control fallback behavior when custom fonts aren't found

Important: Stateless Font Behavior

⚠️ The Swift implementation is stateless - it doesn't remember the previous font settings. This means:

  • DO pass fontFamily (and preferMonospacedFallback if needed) every time you update the title
  • DON'T use setTitle() without font parameters if you want to preserve a custom font

Methods Overview

1. initSystemTray() - Initial Setup

Use this method when first creating the system tray. This is where you set up the initial font.

await systemTray.initSystemTray(
  iconPath: 'icon.png',
  title: "12:00:00",
  fontFamily: "system_monospaced",
  preferMonospacedFallback: true,
);

2. setSystemTrayInfo() - Update Title with Font (Recommended)

Use this method to update the title while preserving or changing the font. This is the recommended method for updating titles with custom fonts.

// Update title with monospaced font
await systemTray.setSystemTrayInfo(
  title: "12:34:56",
  fontFamily: "system_monospaced",
  preferMonospacedFallback: true,
);

3. setTitle() - Convenience Method (Also Works)

The setTitle() method now accepts font parameters for convenience. It's equivalent to setSystemTrayInfo() but with a simpler API.

// Update title with monospaced font
await systemTray.setTitle(
  "12:34:56",
  fontFamily: "system_monospaced",
  preferMonospacedFallback: true,
);

Font Options

System Monospaced Font

The easiest way to get a monospaced font is to use the special "system_monospaced" keyword:

await systemTray.initSystemTray(
  iconPath: 'icon.png',
  title: "12:00:00",
  fontFamily: "system_monospaced",
);

This uses the system's built-in monospaced font (SF Mono on macOS 10.15+, or FixedPitch on older versions).

Custom Fonts

To use a custom font that you've downloaded:

Step 1: Install the Font

  1. Double-click the font file (.ttf, .otf, etc.) to install it in Font Book
  2. Or drag the font file into Font Book

Step 2: Find the Correct Font Name

  1. Open Font Book (Applications → Font Book)
  2. Select your font
  3. Press ⌘+i (or View → Show Font Info)
  4. Use the PostScript name shown (e.g., "FiraCode-Regular", "JetBrainsMono-Regular")
  5. Alternatively, try the family name (e.g., "Fira Code", "JetBrains Mono")

Step 3: Use the Font

// Custom monospaced font
await systemTray.initSystemTray(
  iconPath: 'icon.png',
  title: "12:00:00",
  fontFamily: "FiraCode-Regular", // Use PostScript name
  preferMonospacedFallback: true,  // Fallback to monospaced if font not found
);

// Custom proportional font
await systemTray.initSystemTray(
  iconPath: 'icon.png',
  title: "Hello",
  fontFamily: "Arial",
  preferMonospacedFallback: false, // Fallback to proportional (default)
);

Fallback Behavior

The preferMonospacedFallback parameter controls what happens when a custom font fails to load:

  • preferMonospacedFallback: true → Falls back to system monospaced font
  • preferMonospacedFallback: false (default) → Falls back to standard system font (proportional)

When to Use Each

Use preferMonospacedFallback: true when:

  • You're displaying a clock or timer (needs monospaced)
  • You're using a custom monospaced font
  • You want guaranteed monospaced appearance even if the font fails

Use preferMonospacedFallback: false when:

  • You're using a proportional font (like Arial, Helvetica)
  • You want the standard system font as fallback

Complete Example: Clock Display

Here's a complete example showing how to create and update a clock display:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:system_tray/system_tray.dart';

class ClockTray {
  final SystemTray systemTray = SystemTray();
  Timer? _timer;

  Future<void> init() async {
    // Initialize with monospaced font
    await systemTray.initSystemTray(
      iconPath: 'assets/icon.png',
      title: "00:00:00",
      fontFamily: "system_monospaced",
      preferMonospacedFallback: true,
    );

    // Update every second
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      final now = DateTime.now();
      final timeString = "${now.hour.toString().padLeft(2, '0')}:"
          "${now.minute.toString().padLeft(2, '0')}:"
          "${now.second.toString().padLeft(2, '0')}";
      
      // IMPORTANT: Pass fontFamily every time!
      systemTray.setTitle(
        timeString,
        fontFamily: "system_monospaced",
        preferMonospacedFallback: true,
      );
    });
  }

  void dispose() {
    _timer?.cancel();
  }
}

Common Mistakes

❌ Wrong: Using setTitle() without font parameters

// This will revert to standard font!
systemTray.setTitle("12:00:00"); // Missing fontFamily

✅ Correct: Always pass font parameters

// This preserves the monospaced font
systemTray.setTitle(
  "12:00:00",
  fontFamily: "system_monospaced",
  preferMonospacedFallback: true,
);

❌ Wrong: Setting font only once

// Initial setup
await systemTray.initSystemTray(
  fontFamily: "system_monospaced",
);

// Later update - WRONG! Font is lost
systemTray.setTitle("12:00:00"); // No fontFamily parameter

✅ Correct: Pass font every time

// Initial setup
await systemTray.initSystemTray(
  fontFamily: "system_monospaced",
);

// Later update - CORRECT! Font is preserved
systemTray.setTitle(
  "12:00:00",
  fontFamily: "system_monospaced", // Must pass every time
);

Finding Font Names

Method 1: Font Book (GUI)

  1. Open Font Book
  2. Select your font
  3. Press ⌘+i to show font info
  4. Look for "PostScript name" or "Family name"

Method 2: Terminal (Command Line)

# List all available fonts
fc-list : family

# Search for a specific font
fc-list | grep -i "fira"

Method 3: Programmatic (Swift/macOS)

If you're debugging, you can print available fonts in Swift:

NSFontManager.shared.availableFontFamilies.forEach { print($0) }

Troubleshooting

Font Not Working?

  1. Check if font is installed:

    • Open Font Book
    • Search for your font name
    • If not found, install it first
  2. Try different font name formats:

    • PostScript name: "FiraCode-Regular"
    • Family name: "Fira Code"
    • Full name: "Fira Code Regular"
  3. Check fallback behavior:

    • Set preferMonospacedFallback: true to see if fallback works
    • If fallback works, the issue is with the font name
  4. Verify font is being passed:

    • Make sure you're passing fontFamily every time you update the title
    • Don't use setTitle() without font parameters

Font Reverts to Standard?

This happens when you don't pass fontFamily in an update. Remember:

  • The Swift code is stateless
  • You must pass fontFamily every time you update the title
  • Use setSystemTrayInfo() or setTitle() with font parameters

API Reference

initSystemTray()

Future<bool> initSystemTray({
  required String iconPath,
  String? title,
  String? toolTip,
  bool isTemplate = false,
  String? fontFamily,
  bool preferMonospacedFallback = false,
})

setSystemTrayInfo()

Future<bool> setSystemTrayInfo({
  String? title,
  String? iconPath,
  String? toolTip,
  bool isTemplate = false,
  String? fontFamily,
  bool preferMonospacedFallback = false,
})

setTitle()

Future<void> setTitle(
  String title, {
  String? fontFamily,
  bool preferMonospacedFallback = false,
})

Summary

  • ✅ Use initSystemTray() for initial setup
  • ✅ Use setSystemTrayInfo() or setTitle() with font parameters to update
  • ✅ Always pass fontFamily when updating titles with custom fonts
  • ✅ Use preferMonospacedFallback: true for monospaced fonts
  • ✅ Use "system_monospaced" for the easiest monospaced option
  • ❌ Don't use setTitle() without font parameters if you want custom fonts

- Introduced `fontFamily` and `preferMonospacedFallback` parameters to customize the title's font in the system tray.
- Updated the macOS implementation to handle custom fonts and fallback options.
- Adjusted Dart method signatures and added necessary logic for font handling.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant