settings: emoji search, Manual tab fix, readable infobox (v1.9.5)
- Emoji picker: SearchEntry at top filters all categories via unicodedata.name() in real time; category view hides while searching, restores on clear - Manual tab: add pkg_dir/MANUAL.md to _app_paths() search list so it works in both venv and deb installs (MANUAL.md deployed alongside the package) - bt-infobox CSS: replace @theme_selected_bg_color (saturated blue) with a neutral 5% mix of fg/bg so banner text is always readable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2fe6632269
commit
48be824a50
@ -9,6 +9,21 @@ The version is defined in [`blitztext/__init__.py`](blitztext/__init__.py).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.9.5] - 2026-06-09
|
||||
|
||||
### Added
|
||||
- **Emoji picker search.** A search field at the top of the emoji picker filters
|
||||
all categories in real time using Unicode character names (e.g. "fire", "dog",
|
||||
"heart"). Typing hides the category bar and shows matching results; clearing
|
||||
restores the category view.
|
||||
|
||||
### Fixed
|
||||
- **Manual tab now shows content.** `MANUAL.md` is copied next to the package
|
||||
module so the Manual tab finds it in both venv and deb installs.
|
||||
- **Info banner no longer bright blue.** The `.bt-infobox` background now uses
|
||||
a neutral 5 % tint of the foreground colour instead of the theme accent
|
||||
colour, so text stays readable on any theme.
|
||||
|
||||
## [1.9.4] - 2026-06-09
|
||||
|
||||
### Changed
|
||||
|
||||
@ -6,4 +6,4 @@ counterpart to the macOS Blitztext menu bar app: it runs natively on the host
|
||||
(not in a container) so it can type into any application via xdotool.
|
||||
"""
|
||||
|
||||
__version__ = "1.9.4"
|
||||
__version__ = "1.9.5"
|
||||
|
||||
@ -16,6 +16,7 @@ import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
import gi
|
||||
@ -531,7 +532,7 @@ def _app_paths() -> dict[str, list[Path]]:
|
||||
return {
|
||||
"changelog": [linux_dir / "CHANGELOG.md", Path("/opt/blitztext/CHANGELOG.md")],
|
||||
"license": [repo_dir / "LICENSE", Path("/usr/share/doc/blitztext/copyright")],
|
||||
"manual": [repo_dir / "MANUAL.md", Path("/opt/blitztext/MANUAL.md")],
|
||||
"manual": [pkg_dir / "MANUAL.md", repo_dir / "MANUAL.md", Path("/opt/blitztext/MANUAL.md")],
|
||||
}
|
||||
|
||||
|
||||
@ -588,8 +589,8 @@ class SettingsDialog:
|
||||
}
|
||||
.bt-infobox {
|
||||
border-radius: 6px;
|
||||
border: 1px solid mix(@theme_selected_bg_color, @theme_bg_color, 0.55);
|
||||
background-color: mix(@theme_selected_bg_color, @theme_base_color, 0.07);
|
||||
border: 1px solid mix(@theme_fg_color, @theme_bg_color, 0.75);
|
||||
background-color: mix(@theme_fg_color, @theme_bg_color, 0.05);
|
||||
padding: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@ -819,7 +820,26 @@ class SettingsDialog:
|
||||
pop = Gtk.Popover(relative_to=anchor)
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||
|
||||
# -- category tab bar ------------------------------------------------
|
||||
# -- Search bar -------------------------------------------------------
|
||||
search = Gtk.SearchEntry()
|
||||
search.set_placeholder_text("Search emoji…")
|
||||
search.set_margin_top(6); search.set_margin_bottom(4)
|
||||
search.set_margin_start(6); search.set_margin_end(6)
|
||||
vbox.pack_start(search, False, False, 0)
|
||||
vbox.pack_start(Gtk.Separator(), False, False, 0)
|
||||
|
||||
# Flat emoji list (all categories) used by search
|
||||
all_emojis: list[str] = [e for _, _, es in _EMOJI_CATEGORIES for e in es]
|
||||
|
||||
def _keywords(e: str) -> str:
|
||||
try:
|
||||
return unicodedata.name(e[0], "").lower()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
# -- Category view ----------------------------------------------------
|
||||
cat_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||
|
||||
stack = Gtk.Stack()
|
||||
stack.set_transition_type(Gtk.StackTransitionType.NONE)
|
||||
stack.set_size_request(420, 230)
|
||||
@ -829,7 +849,7 @@ class SettingsDialog:
|
||||
cat_bar.set_margin_start(4); cat_bar.set_margin_end(4)
|
||||
|
||||
first_name: str | None = None
|
||||
active_btn: list[Gtk.Button] = [None] # mutable cell
|
||||
active_btn: list[Gtk.Button] = [None]
|
||||
|
||||
def _select(name: str, btn: Gtk.Button) -> None:
|
||||
stack.set_visible_child_name(name)
|
||||
@ -839,7 +859,6 @@ class SettingsDialog:
|
||||
active_btn[0] = btn
|
||||
|
||||
for cat_emoji, cat_name, emojis in _EMOJI_CATEGORIES:
|
||||
# Build scrollable emoji grid for this category
|
||||
flow = Gtk.FlowBox()
|
||||
flow.set_max_children_per_line(10)
|
||||
flow.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||
@ -855,7 +874,6 @@ class SettingsDialog:
|
||||
sw.add(flow)
|
||||
stack.add_named(sw, cat_name)
|
||||
|
||||
# Category tab button
|
||||
cb = Gtk.Button(label=cat_emoji)
|
||||
cb.set_relief(Gtk.ReliefStyle.NONE)
|
||||
cb.set_tooltip_text(cat_name)
|
||||
@ -874,12 +892,46 @@ class SettingsDialog:
|
||||
cat_scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
|
||||
cat_scroll.set_min_content_height(44)
|
||||
cat_scroll.add(cat_bar)
|
||||
cat_container.pack_start(cat_scroll, False, False, 0)
|
||||
cat_container.pack_start(Gtk.Separator(), False, False, 0)
|
||||
cat_container.pack_start(stack, True, True, 0)
|
||||
|
||||
vbox.pack_start(cat_scroll, False, False, 0)
|
||||
vbox.pack_start(Gtk.Separator(), False, False, 0)
|
||||
vbox.pack_start(stack, True, True, 0)
|
||||
# -- Search results view ----------------------------------------------
|
||||
search_sw = Gtk.ScrolledWindow()
|
||||
search_sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
||||
search_sw.set_size_request(420, 274)
|
||||
search_flow = Gtk.FlowBox()
|
||||
search_flow.set_max_children_per_line(10)
|
||||
search_flow.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||
search_flow.set_margin_top(4); search_flow.set_margin_bottom(4)
|
||||
search_flow.set_margin_start(4); search_flow.set_margin_end(4)
|
||||
search_sw.add(search_flow)
|
||||
|
||||
def _on_search(_e: Gtk.SearchEntry) -> None:
|
||||
query = search.get_text().strip().lower()
|
||||
for child in search_flow.get_children():
|
||||
search_flow.remove(child)
|
||||
if query:
|
||||
cat_container.set_visible(False)
|
||||
search_sw.set_visible(True)
|
||||
for emoji in all_emojis:
|
||||
if query in _keywords(emoji) or query in emoji:
|
||||
btn = Gtk.Button(label=emoji)
|
||||
btn.set_relief(Gtk.ReliefStyle.NONE)
|
||||
btn.connect("clicked", lambda _b, e=emoji: (entry.set_text(e), pop.popdown()))
|
||||
search_flow.add(btn)
|
||||
search_flow.show_all()
|
||||
else:
|
||||
search_sw.set_visible(False)
|
||||
cat_container.set_visible(True)
|
||||
|
||||
search.connect("search-changed", _on_search)
|
||||
|
||||
vbox.pack_start(cat_container, True, True, 0)
|
||||
vbox.pack_start(search_sw, True, True, 0)
|
||||
pop.add(vbox)
|
||||
pop.show_all()
|
||||
search_sw.set_visible(False) # hide search results until user types
|
||||
|
||||
# ===== Engines ==========================================================
|
||||
def _build_engines(self, page: Gtk.Box) -> None:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user