init commit
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# Mode indicator
|
||||
|
||||
Graphical indicator to show you which Talon mode your currently are in. Supports a lot of settings to move, resize and change colors.
|
||||
|
||||
## Default colors
|
||||
|
||||
Command mode
|
||||
|
||||

|
||||
|
||||
Dictation mode
|
||||
|
||||

|
||||
|
||||
Mixed mode
|
||||
|
||||

|
||||
|
||||
Sleep mode
|
||||
|
||||

|
||||
|
||||
Other modes
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
You can enable this by changing the following setting in `mode_indicator.talon`:
|
||||
`user.mode_indicator_show = true`
|
||||
|
||||
## Demo
|
||||
|
||||
[YouTube - Mode indicator demo](https://youtu.be/1lqtfM4vvH4)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,228 @@
|
||||
from talon import Module, actions, app, cron, registry, scope, settings, skia, ui
|
||||
from talon.canvas import Canvas
|
||||
from talon.screen import Screen
|
||||
from talon.skia.canvas import Canvas as SkiaCanvas
|
||||
from talon.skia.imagefilter import ImageFilter
|
||||
from talon.ui import Point2d, Rect
|
||||
|
||||
canvas: Canvas = None
|
||||
current_mode = ""
|
||||
current_microphone = ""
|
||||
mod = Module()
|
||||
|
||||
mod.setting(
|
||||
"mode_indicator_show",
|
||||
type=bool,
|
||||
default=False,
|
||||
desc="If true the mode indicator is shown",
|
||||
)
|
||||
mod.setting(
|
||||
"mode_indicator_show_microphone_name",
|
||||
type=bool,
|
||||
default=False,
|
||||
desc="Show first two letters of microphone name if true",
|
||||
)
|
||||
mod.setting(
|
||||
"mode_indicator_size",
|
||||
type=float,
|
||||
desc="Mode indicator diameter in pixels",
|
||||
)
|
||||
mod.setting(
|
||||
"mode_indicator_x",
|
||||
type=float,
|
||||
desc="Mode indicator center X-position in percentages(0-1). 0=left, 1=right",
|
||||
)
|
||||
mod.setting(
|
||||
"mode_indicator_y",
|
||||
type=float,
|
||||
desc="Mode indicator center Y-position in percentages(0-1). 0=top, 1=bottom",
|
||||
)
|
||||
mod.setting(
|
||||
"mode_indicator_color_alpha",
|
||||
type=float,
|
||||
desc="Mode indicator alpha/opacity in percentages(0-1). 0=fully transparent, 1=fully opaque",
|
||||
)
|
||||
mod.setting(
|
||||
"mode_indicator_color_gradient",
|
||||
type=float,
|
||||
desc="Mode indicator gradient brightness in percentages(0-1). 0=darkest, 1=brightest",
|
||||
)
|
||||
mod.setting("mode_indicator_color_text", type=str)
|
||||
mod.setting("mode_indicator_color_mute", type=str)
|
||||
mod.setting("mode_indicator_color_sleep", type=str)
|
||||
mod.setting("mode_indicator_color_dictation", type=str)
|
||||
mod.setting("mode_indicator_color_mixed", type=str)
|
||||
mod.setting("mode_indicator_color_command", type=str)
|
||||
mod.setting("mode_indicator_color_other", type=str)
|
||||
|
||||
setting_paths = {
|
||||
"user.mode_indicator_show",
|
||||
"user.mode_indicator_size",
|
||||
"user.mode_indicator_x",
|
||||
"user.mode_indicator_y",
|
||||
"user.mode_indicator_color_alpha",
|
||||
"user.mode_indicator_color_gradient",
|
||||
"user.mode_indicator_color_mute",
|
||||
"user.mode_indicator_color_sleep",
|
||||
"user.mode_indicator_color_dictation",
|
||||
"user.mode_indicator_color_mixed",
|
||||
"user.mode_indicator_color_command",
|
||||
"user.mode_indicator_color_other",
|
||||
}
|
||||
|
||||
|
||||
def get_mode_color() -> str:
|
||||
if current_microphone == "None":
|
||||
return settings.get("user.mode_indicator_color_mute")
|
||||
if current_mode == "sleep":
|
||||
return settings.get("user.mode_indicator_color_sleep")
|
||||
elif current_mode == "dictation":
|
||||
return settings.get("user.mode_indicator_color_dictation")
|
||||
elif current_mode == "mixed":
|
||||
return settings.get("user.mode_indicator_color_mixed")
|
||||
elif current_mode == "command":
|
||||
return settings.get("user.mode_indicator_color_command")
|
||||
else:
|
||||
return settings.get("user.mode_indicator_color_other")
|
||||
|
||||
|
||||
def get_alpha_color() -> str:
|
||||
return f"{int(settings.get('user.mode_indicator_color_alpha') * 255):02x}"
|
||||
|
||||
|
||||
def get_gradient_color(color: str) -> str:
|
||||
factor = settings.get("user.mode_indicator_color_gradient")
|
||||
# hex -> rgb
|
||||
(r, g, b) = tuple(int(color[i : i + 2], 16) for i in (0, 2, 4))
|
||||
# Darken rgb
|
||||
r, g, b = int(r * factor), int(g * factor), int(b * factor)
|
||||
# rgb -> hex
|
||||
return f"{r:02x}{g:02x}{b:02x}"
|
||||
|
||||
|
||||
def get_colors():
|
||||
color_mode = get_mode_color()
|
||||
color_gradient = get_gradient_color(color_mode)
|
||||
color_alpha = get_alpha_color()
|
||||
color_text = settings.get("user.mode_indicator_color_text")
|
||||
return f"{color_mode}{color_alpha}", color_gradient, color_text
|
||||
|
||||
|
||||
def on_draw(c: SkiaCanvas):
|
||||
color_mode, color_gradient, color_text = get_colors()
|
||||
x, y = c.rect.center.x, c.rect.center.y
|
||||
radius = c.rect.height / 2 - 2
|
||||
|
||||
c.paint.shader = skia.Shader.radial_gradient(
|
||||
Point2d(x, y), radius, [color_mode, color_gradient]
|
||||
)
|
||||
|
||||
c.paint.imagefilter = ImageFilter.drop_shadow(1, 1, 1, 1, color_gradient)
|
||||
|
||||
c.paint.style = c.paint.Style.FILL
|
||||
c.paint.color = color_mode
|
||||
c.draw_circle(x, y, radius)
|
||||
|
||||
if settings.get("user.mode_indicator_show_microphone_name"):
|
||||
# Remove c.paint.shader gradient before drawing again
|
||||
c.paint.shader = skia.Shader.radial_gradient(
|
||||
Point2d(x, y), radius, [color_text, color_text]
|
||||
)
|
||||
|
||||
text = current_microphone[:2]
|
||||
c.paint.style = c.paint.Style.FILL
|
||||
c.paint.color = color_text
|
||||
text_rect = c.paint.measure_text(text)[1]
|
||||
c.draw_text(
|
||||
text,
|
||||
x - text_rect.center.x,
|
||||
y - text_rect.center.y,
|
||||
)
|
||||
|
||||
|
||||
def move_indicator():
|
||||
screen: Screen = ui.main_screen()
|
||||
rect = screen.rect
|
||||
scale = screen.scale if app.platform != "mac" else 1
|
||||
radius = settings.get("user.mode_indicator_size") * scale / 2
|
||||
|
||||
x = rect.left + min(
|
||||
max(settings.get("user.mode_indicator_x") * rect.width - radius, 0),
|
||||
rect.width - 2 * radius,
|
||||
)
|
||||
|
||||
y = rect.top + min(
|
||||
max(settings.get("user.mode_indicator_y") * rect.height - radius, 0),
|
||||
rect.height - 2 * radius,
|
||||
)
|
||||
|
||||
side = 2 * radius
|
||||
canvas.resize(side, side)
|
||||
canvas.move(x, y)
|
||||
|
||||
|
||||
def show_indicator():
|
||||
global canvas
|
||||
canvas = Canvas.from_rect(Rect(0, 0, 0, 0))
|
||||
canvas.register("draw", on_draw)
|
||||
|
||||
|
||||
def hide_indicator():
|
||||
global canvas
|
||||
canvas.unregister("draw", on_draw)
|
||||
canvas.close()
|
||||
canvas = None
|
||||
|
||||
|
||||
def update_indicator():
|
||||
if settings.get("user.mode_indicator_show"):
|
||||
if not canvas:
|
||||
show_indicator()
|
||||
move_indicator()
|
||||
canvas.freeze()
|
||||
elif canvas:
|
||||
hide_indicator()
|
||||
|
||||
|
||||
def on_update_contexts():
|
||||
global current_mode
|
||||
modes = scope.get("mode")
|
||||
if "sleep" in modes:
|
||||
mode = "sleep"
|
||||
elif "dictation" in modes:
|
||||
if "command" in modes:
|
||||
mode = "mixed"
|
||||
else:
|
||||
mode = "dictation"
|
||||
elif "command" in modes:
|
||||
mode = "command"
|
||||
else:
|
||||
mode = "other"
|
||||
|
||||
if current_mode != mode:
|
||||
current_mode = mode
|
||||
update_indicator()
|
||||
|
||||
|
||||
def on_update_settings(updated_settings: set[str]):
|
||||
if setting_paths & updated_settings:
|
||||
update_indicator()
|
||||
|
||||
|
||||
def poll_microphone():
|
||||
# Ideally, we would have a callback instead of needing to poll. https://github.com/talonvoice/talon/issues/624
|
||||
global current_microphone
|
||||
microphone = actions.sound.active_microphone()
|
||||
if current_microphone != microphone:
|
||||
current_microphone = microphone
|
||||
update_indicator()
|
||||
|
||||
|
||||
def on_ready():
|
||||
registry.register("update_contexts", on_update_contexts)
|
||||
registry.register("update_settings", on_update_settings)
|
||||
ui.register("screen_change", lambda _: update_indicator)
|
||||
cron.interval("500ms", poll_microphone)
|
||||
|
||||
|
||||
app.register("ready", on_ready)
|
||||
@@ -0,0 +1,29 @@
|
||||
settings():
|
||||
# Don't show mode indicator by default
|
||||
user.mode_indicator_show = false
|
||||
# Set to true to show the first 2 letters of the microphone name inside the mode indicator
|
||||
user.mode_indicator_show_microphone_name = false
|
||||
# 30pixels diameter
|
||||
user.mode_indicator_size = 30
|
||||
# Center horizontally. (0=left, 0.5=center, 1=right)
|
||||
user.mode_indicator_x = 0.5
|
||||
# Align top. (0=top, 0.5=center, 1=bottom)
|
||||
user.mode_indicator_y = 0
|
||||
# Slightly transparent
|
||||
user.mode_indicator_color_alpha = 0.75
|
||||
# Grey gradient
|
||||
user.mode_indicator_color_gradient = 0.5
|
||||
# White color for optional text overlay on mode indicator
|
||||
user.mode_indicator_color_text = "eeeeee"
|
||||
# Black color for when the microphone is muted (set to "None")
|
||||
user.mode_indicator_color_mute = "000000"
|
||||
# Grey color for sleep mode
|
||||
user.mode_indicator_color_sleep = "808080"
|
||||
# Gold color for dictation mode
|
||||
user.mode_indicator_color_dictation = "ffd700"
|
||||
# MediumSeaGreen color for mixed mode
|
||||
user.mode_indicator_color_mixed = "3cb371"
|
||||
# CornflowerBlue color for command mode
|
||||
user.mode_indicator_color_command = "6495ed"
|
||||
# GhostWhite color for other modes
|
||||
user.mode_indicator_color_other = "f8f8ff"
|
||||
Reference in New Issue
Block a user