Skip to content

Commit

Permalink
Added programming icon poller inspired by ideas and implementation of…
Browse files Browse the repository at this point in the history
… wenkokke

Split up the status bar pollers into different pollers that can be turned on individually
Finished refactoring in the new content API and removed references to the old API
Added configuration file to put in the preferences that are used for things like paths etc
Added persistable prefered microphone
Added programming icons from various icon packs laid out in the README
Chose a more consistent style - walkthrough instead of walk_through and double quotes instead of single
  • Loading branch information
chaosparrot committed Feb 20, 2022
1 parent ba587bb commit b11f456
Show file tree
Hide file tree
Showing 52 changed files with 923 additions and 1,011 deletions.
57 changes: 31 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,10 @@ These are ideas that I want to implement in no specific order and with no specif

Known issues
---
- Walkthrough progress styling, and an overview of all the available steps
- 'Dismiss options' in the context menu makes the HUD go into a state where the next context menu won't respond properly
- Walkthrough text clipping
- Walkthrough animations not registering all the time
- Walkthrough voice commands not activating animation

If any of these ideas seem cool for you to work on, give me a message on the talon slack so we can coordinate stuff.

Expand All @@ -225,55 +227,56 @@ This is the current status of it
- [x] Automatically enable pollers on start up when a widget is listening for it
- [ ] Connect automatic enabling / disabling to the Talon HUD environment flow
- [x] Add a new content builder class to build HUD content with inside of pollers so they do not require actions
- [ ] Rework all the pollers to not use actions in enable / disable flow and have the content builder inserted
- [x] Rework all the pollers to not use actions in enable / disable flow and have the content builder inserted
- [x] History poller
- [x] Focus poller
- [x] Speech poller
- [x] Scope poller
- [x] List poller
- [x] Microphone poller
- [ ] Walkthrough
- [ ] Add a settings file persisting thingie specifically for content like walkthrough pollers and the favourite microphone
- [ ] Persist walkthrough
- [ ] Persist favourite microphone
- [ ] Persist debug list
- [ ] Phase out status bar poller in favour of the following:
- [x] Walkthrough
- [x] Add a settings file persisting mechanism specifically for content like walkthrough pollers and the favourite microphone
- [x] Persist walkthrough
- [x] Persist favourite microphone
- [x] Phase out status bar poller in favour of the following:
- [x] Active microphone status icon poller
- [ ] Mode icon poller
- [ ] Make overrideable action for determining modes
- [ ] Make overrideable action for switching modes
- [ ] Programming language poller
- [ ] Make list of languages in preferences for user to change
- [ ] Add programming icons
- [x] Mode icon poller
- [x] Make overrideable action for determining modes
- [x] Make overrideable action for switching modes
- [x] Programming language poller
- [x] Make list of languages in preferences for user to change
- [x] Add programming icons
- [x] Language poller
- [x] Focus icon
- [ ] Draw text in status bar again if no icon is found for programming language
- [ ] Fix initial status bar animation
- [ ] Manage topics on widgets properly so that the poller state can be managed
- [ ] Fix inconsistent topic registering / deregistering for single topic widgets vs multitopic widgets
- [ ] Fix topic deregistering if no content is available for it
- [ ] Fix ordering of topics properly
- [X] Remove single widget.topic variable
- [ ] Rework variables like the 'mode' check to use the new events
- [ ] Rework all widgets to conform to the new events
- [x] Remove single widget.topic variable
- [ ] Make the HUD sort of reloadable by reattaching pollers, content etc
- [x] Rework variables like the 'mode' check to use the new events
- [x] Rework all widgets to conform to the new events
- [x] Status bar
- [x] Event log
- [x] Ability bar
- [x] Text panel
- [x] Documentation panel
- [x] Choice panel
- [ ] Walkthrough panel
- [x] Walkthrough panel
- [x] Screen overlay
- [x] Cursor tracker
- [ ] Phase out references to old API in state.py and display.py
- [ ] Remove unnecessary refreshing of content
- [x] Phase out references to old API in state.py and display.py
- [x] Remove unnecessary refreshing of content
- [ ] Consistent style
- [ ] " quotes instead of '
- [ ] Choose one walkthrough definition ( walkthrough or walk through, offer automatic migration )
- [x] " quotes instead of '
- [x] Choose one walkthrough definition
- [ ] Clear context and walk_through options - Migration version in preferences?
- [ ] Make sure all HUD actions are prefixed with hud_ and keep a list of changes for the release notes
- [x] Use only relative paths to make it more flexible where the HUD is located
- [ ] Update documentation for status bar icons and options
- [ ] Update documentation for pollers
- [ ] Update documentation on mode icons
- [ ] Update documentation on active microphone
- [ ] Redo documentation for all content / widgets etc
- [ ] Clean up Audio stuff - This is for the next release

Acknowledgements
---
Expand All @@ -283,3 +286,5 @@ Some of the icons like the copy icon are taken from fontawesome.

The language icons are made by https://www.freepik.com hosted on https://www.flaticon.com/.
If your language isn't in the themes, you can download icons for free with attribution of freepik.com and change their size to 35x35 before saving them as a .png file in your theme.

The programming language icons are taken from various icon packs, including Pictonic Icons, Material design, File by John Gardner and LibreICONS Black by DiemenDesign
24 changes: 24 additions & 0 deletions _configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from talon import Module
from typing import Any
import os

# Defaults
preferences_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "preferences")

# This file is used for all the configurations like custom paths etc
hud_configuration = {
"user_preferences_folder": preferences_dir,
"content_preferences_folder": preferences_dir,
}

def hud_get_configuration(title: str, default = None) -> Any:
global hud_configuration
return hud_configuration[title] if title in hud_configuration else default

mod = Module()
@mod.action_class
class Actions:

def hud_get_configuration(title: str, default: Any = None) -> Any:
"""Get a configuration setting from the HUD setting object"""
return hud_get_configuration(title, default)
28 changes: 8 additions & 20 deletions base_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,10 @@ class BaseWidget(metaclass=ABCMeta):

allowed_setup_options = ["position", "dimension", "limit", "font_size"]

subscribed_content = ['mode']

# New content topic types
topic_types = []
current_topics = []

current_topics = []
contentv2 = None
content = {}

animation_tick = 0
animation_max_duration = 100
Expand Down Expand Up @@ -120,28 +116,20 @@ def content_handler(self, event) -> bool:
self.preferences.mark_changed = True
self.event_dispatch.request_persist_preferences()

if not self.sleep_enabled and event.topic_type == "variable" and event.topic == "mode":
if (event.content == "sleep"):
self.disable()
elif self.preferences.enabled == True:
self.enable()

self.refresh({"event": event})

updated = False
if self.enabled and self.canvas:
self.canvas.resume()
updated = True
return updated

def update_content(self, content):
if not self.sleep_enabled and "mode" in content:
if (content["mode"] == "sleep"):
self.disable()
elif self.preferences.enabled == True:
self.enable()

self.refresh(content)
for key in content:
self.content[key] = content[key]

if self.enabled and self.canvas:
self.canvas.resume()


def update_panel(self, panel_content) -> bool:
if not panel_content.content[0] and self.enabled:
self.disable()
Expand Down
8 changes: 6 additions & 2 deletions content/content_builder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .state import HeadUpDisplayContent
from .typing import *
from typing import Union, Any, Callable
from ..utils import retrieve_available_voice_commands
import time

# Class for managing a part of the HUD content
Expand Down Expand Up @@ -58,7 +59,10 @@ def create_choices(self, choices_list: list[Any], callback: Callable[[Any], None

def create_walkthrough_step(self, content: str, context_hint: str = "", tags: list[str] = None, modes: list[str] = None, app: str = "", restore_callback: Callable[[str], None] = None) -> HudWalkThroughStep:
"""Create a walkthrough step used inside of a walkthrough in the Talon HUD"""
return HudWalkThroughStep(content, context_hint, tags, modes, app, None, restore_callback)
voice_commands = retrieve_available_voice_commands(content)
tags = [] if tags is None else tags
modes = [] if modes is None else modes
return HudWalkThroughStep(content, context_hint, tags, modes, app, voice_commands, restore_callback)

def create_walkthrough(self, title, walkthrough_steps: list[HudWalkThroughStep]) -> HudWalkThrough:
"""Create a walkthrough for the Talon HUD"""
Expand All @@ -75,4 +79,4 @@ def publish_event(self, topic_type: str, topic:str, operation: str, data: Any =

def connect(self, content: HeadUpDisplayContent = None):
"""Connect the current content builder to a new central content builder"""
self._content = content
self._content = content
8 changes: 7 additions & 1 deletion content/history_poller.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ def on_phrase(self, j):
"microphone": mic
}

self.content.add_log("phrase", command, timestamp, metadata)
self.content.add_log("phrase", command, timestamp, metadata)

def on_ready():
# This poller needs to be kept alive so that the phrases are properly registered
actions.user.hud_add_poller("history", HistoryPoller(), True)

app.register("ready", on_ready)
6 changes: 3 additions & 3 deletions content/language_poller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class LanguagePoller(Poller):

def enable(self):
if (self.job is None):
self.job = cron.interval('200ms', self.language_check)
self.job = cron.interval("200ms", self.language_check)

def disable(self):
if self.enabled:
Expand All @@ -18,15 +18,15 @@ def disable(self):
self.job = None

def language_check(self):
language = scope.get('language')
language = scope.get("language")
if self.current_language != language:
self.current_language = language
status_icon = self.content.create_status_icon("language_toggle", language if language != "en_US" else None, None, "Language " + language, lambda _, _2: toggle_language)
self.content.publish_event("status_icons", status_icon.topic, "replace", status_icon, False)

def toggle_language():
# TODO THIS NO LONGER SEEMS TO WORK?
actions.speech.switch_language('en_US')
actions.speech.switch_language("en_US")

def add_statusbar_language_icon(_ = None):
actions.user.hud_activate_poller("language_toggle")
Expand Down
4 changes: 2 additions & 2 deletions content/list_poller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
class ListPoller(Poller):
content = None
job = None
previous_list_state = ''
previous_list_state = ""
list = None
should_open = False

def enable(self):
if (self.job is None):
if self.job is None and self.list is not None:
self.enabled = True
self.should_open = True
self.job = cron.interval("200ms", self.list_check)
Expand Down
45 changes: 35 additions & 10 deletions content/microphone_poller.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from talon import actions, cron, scope, speech_system, ui, app, Module
from .poller import Poller
from .._configuration import hud_get_configuration
import os
prefered_microphone = None


# Polls the current microphone state
class MicrophonePoller(Poller):
Expand All @@ -9,42 +13,62 @@ class MicrophonePoller(Poller):
def register(self, name, callback):
current_callback_amount = len(self.callbacks.values())
self.callbacks[name] = callback
if (current_callback_amount == 0):
if current_callback_amount == 0:
self.job = cron.interval("300ms", self.state_check)

def unregister(self, name):
if name in self.callbacks:
del self.callbacks[name]

current_callback_amount = len(self.callbacks.values())
if (current_callback_amount == 0):
if current_callback_amount == 0:
cron.cancel(self.job)
self.job = None

def state_check(self):
active_mic = actions.sound.active_microphone()
microphones = actions.sound.microphones() if "microphone_list" in self.callbacks else []

callbacks = self.callbacks.values()
callbacks = list(self.callbacks.values())[:]
for callback in callbacks:
callback(active_mic, microphones)

previous_mic_file = os.path.join(hud_get_configuration("content_preferences_folder"), "hud_prefered_microphone.txt")
def set_prefered_microphone(microphone):
global prefered_microphone
global previous_mic_file
prefered_microphone = microphone
with open(previous_mic_file, "w") as f:
f.write(microphone)

def get_prefered_microphone():
previous_mic_file
prefered_microphone = "System Default"
if not os.path.exists(previous_mic_file):
set_prefered_microphone(prefered_microphone)

with open(previous_mic_file, "r") as file:
lines = file.readlines()
if len(lines) > 0:
prefered_microphone = lines[0]
return prefered_microphone
prefered_microphone = get_prefered_microphone()

previous_microphone = None # TODO PROPER PERSISTING AND FAVORITING OF MICROPHONE SELECTION
def toggle_microphone(self, _ = None):
global previous_microphone
global prefered_microphone
current_microphone = actions.sound.active_microphone()
if current_microphone != "None":
actions.sound.set_microphone("None")
previous_microphone = current_microphone
else:
if previous_microphone == "None":
previous_microphone = "System Default"
actions.sound.set_microphone(previous_microphone)
if prefered_microphone in actions.sound.microphones():
actions.sound.set_microphone(prefered_microphone)
else:
actions.user.hud_add_log("warning", "Could not find " + prefered_microphone + ".\nUsing system default")
actions.sound.set_microphone("System Default")

def select_microphone(choice):
set_prefered_microphone(choice["text"])
actions.sound.set_microphone(choice["text"])
# TODO PROPER PERSISTING AND FAVORITING OF MICROPHONE SELECTION
actions.user.hud_deactivate_poller("microphone_list")

class PartialMicrophonePoller(Poller):
Expand All @@ -61,6 +85,7 @@ def __init__(self, type, poller: MicrophonePoller):
def enable(self):
if not self.enabled:
self.enabled = True
self.current_microphone = None
self.poller.register(self.type, self.update_microphone)

def update_microphone(self, active_microphone, microphones):
Expand Down
Loading

0 comments on commit b11f456

Please sign in to comment.