Sublime Forum

Yay to "choose font" in 4156! Command palette?

#1

Just updated and saw that fonts can be picked without guessing their title format. This is awesome! :tada: I always struggled translating font name to proper JSON value.

What would be even more awesome is if we had a command palette font selector.

I believe this can be trickier than it looks, due to how fonts are discovered on various systems, and the fact that “choose font” delegates to the native font selection tool. But it would be such a perfect use case for command palette, and a big convenience boost.

0 Likes

#2

The big advantage the native dialogs have is a preview of the font. That’s not feasible to do in the command palette.

0 Likes

#3

Well, I only partly agree on that.

I personally don’t use the normal main menu anymore at all and am very happy about the hamburger menu reducing it to the least.

Hence a Command Pallet approach would be my favorite as well.

Based on a suggestion to choose from predefined fonts, I’ve created a POC after the scheme of UI: Select Color Scheme... - live preview included. Actually, many parts of said command can be re-used. The POC is somewhat more simple atm. without resetting view-specific fonts. The only missing element was an API call which lists available fonts from OS. Instead a "fonts": [] setting is used to quickly switch between fonts as needed.

I find such a simple and quick approach useful especially as switching between light and dark color schemes often requires to change between a “normal” and “light” font variant due to fonts appearing much more bold in dark mode.

Default.sublime-commands

[
	{
		"caption": "UI: Select Font…",
		"command": "select_font"
	},
]

select_font.py

import sublime
import sublime_plugin

PREFS_FILE = 'Preferences.sublime-settings'

CURRENT_KIND = (sublime.KIND_ID_COLOR_GREENISH, "✓", "Current")


class FontInputHandler(sublime_plugin.ListInputHandler):

    def __init__(self):
        super().__init__()
        self.prefs = sublime.load_settings(PREFS_FILE)
        self.original = self.prefs.get("font_face", "")

    def placeholder(self):
        return "Select Font"

    def cancel(self):
        self.prefs.set('font_face', self.original)
        sublime.save_settings(PREFS_FILE)

    def confirm(self):
        sublime.save_settings(PREFS_FILE)

    def preview(self, font_face):
        if font_face is None:
            return

        self.last_previewed = font_face

        def update():
            # The color scheme to preview has been updated since
            # the timeout was created
            if font_face != self.last_previewed:
                return
            if self.prefs.get('font_face') == font_face:
                return
            self.prefs.set('font_face', font_face)

        sublime.set_timeout(update, 250)

    def list_items(self):
        fonts = self.prefs.get('fonts')
        fonts = set(fonts) if isinstance(fonts, list) else set()
        fonts.add(self.prefs.get("font_face"))

        items = []
        selected = -1
        for font in sorted(fonts):
            kind_info = sublime.KIND_AMBIGUOUS
            if self.original and self.original == font:
                kind_info = CURRENT_KIND
                selected = len(items)

            items.append(sublime.ListInputItem(font, font, kind=kind_info))

        return (items, selected)


class SelectFontCommand(sublime_plugin.WindowCommand):

    def input_description(self):
        return "Font:"

    def input(self, args):
        return FontInputHandler()

    def run(self, font_face):
        settings = sublime.load_settings(PREFS_FILE)
        settings.set('font_face', font_face)
        sublime.save_settings(PREFS_FILE)

2 Likes

#4

I tested out for MacOS (Linux probably as well)

adding the following function to populate the fonts (Although this includes a ton!)

import subprocess

def get_fonts():
    command = 'fc-list'
    process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
    output, error = process.communicate()

    if error:
        return []

    output = output.decode("utf-8").split('\n')
    return [name.strip() for font in output if font for name in font.split(':')[1].split(',')]

This results in a nice list

0 Likes