update the kaomoji / emote selector

This commit is contained in:
2025-12-02 07:34:29 +01:00
parent 5456b6c5fd
commit d5a4628281

View File

@@ -1,4 +1,4 @@
import wx
import wx , wx.adv
import threading
import logging
from SearchDialog import SearchDialog
@@ -396,34 +396,67 @@ class IRCPanel(wx.Panel):
logger.error(f"Error inserting text at caret: {e}")
def on_pick_kaomoji(self, event):
"""Show a kaomoji popup next to the button and insert the chosen one.
All entries are ASCII-only so they are safe for any IRC server.
"""
Show a grouped kaomoji popup next to the button and insert the chosen one.
"""
try:
choices = [
":-)", ":)", ":-D", ":D", "^_^", "^o^", "(*^_^*)", "( ^_^)", "(:3)", "=)", "=]", "^.^",
":-(", ":'(", "T_T", ";_;", ">_<", "(-_-)", "(_ _)",
">:(", ">:-(", ">:-O", ">.<", "(-_-)#",
"<3", "(*^3^)", "(^^)v", "(X_X)", "(^_^)", "*^_^*",
":-O", ":O", ":-0", "O_O", "o_O", "O_o",
"-_-", "(-.-) zzz", "(~_~)", "zzz",
r"¯\_(._.)_/¯", "(¬_¬)", "(*_*)", "(>_>)", "(<_<)",
"OwO", "UwU", ">w<", "^w^", "^u^", "rawr x3", ":3", ":3c", "x3", "nya~", "n_n", "(>ω<)", ":33", "^3^",
"^///^", "(//▽//)", "(*^///^*)", ">///<", "^_^;", "^///^;",
"(*^▽^*)", "(*´▽`*)", "UwU~", "OwO~",
":33", "x3", ":3~", ":3c", "owo", "uwu", "rawr", ":33p",
"xD", ";-)", ";)", ":-P", ":P", ":-|", ":|", "(o_O)", "(O_o)", "('_')",
]
kaomoji_groups = {
"Happy": [
":-)", ":)", ":-D", ":D", "^_^", "^o^", "(*^_^*)", "( ^_^)", "(:3)", "=)", "=]", "^.^",
"UwU", "OwO", "^w^", "^u^", "x3", ":3", ":3c", "nya~", "n_n", "(>ω<)", ":33", "^3^"
],
"Sad": [
":-(", ":'(", "T_T", ";_;", ">_<", "(-_-)", "(_ _)",
],
"Angry": [
">:(", ">:-(", ">:-O", ">.<", "(-_-)#"
],
"Love": [
"<3", "(*^3^)", "(^^)v", "(^_^)", "*^_^*"
],
"Surprised / Misc": [
":-O", ":O", ":-0", "O_O", "o_O", "O_o"
],
"Sleepy | Bored": [
"-_-", "(-.-) zzz", "(~_~)", "zzz"
],
"Gay": [
r"¯\_(._.)_/¯", "(¬_¬)", "(*_*)", "(>_>)", "(<_<)", "(o_O)", "(O_o)", "('_')",
"(//▽//)", "(*^///^*)", ">///<", "^_^;", "^///^;", "owo", "uwu", "rawr", ":33p"
],
"Blush": [
"^///^", "(//▽//)", "(*^///^*)", ">///<", "^_^;", "^///^;",
"(*^▽^*)", "(*´▽`*)"
],
"Others": [
"UwU~", "OwO~", ":33", "x3", ":3~", ":3c", "rawr x3", "xD", ";-)", ";)", ":-P", ":P", ":-|", ":|"
]
}
ascii_choices = [c for c in choices if all(ord(ch) < 128 for ch in c)]
if not ascii_choices:
ascii_choices = [":)", ":D", ";)", ":P"]
display_items = []
item_lookup = [] # tuple: (group, index in group) or None for separators/labels
# Helper for ASCII filtering
def is_ascii(s):
return all(ord(ch) < 128 for ch in s)
for group, choices in kaomoji_groups.items():
group_ascii_choices = [c for c in choices if is_ascii(c)]
if not group_ascii_choices:
continue
display_items.append(f"──────────── {group} ────────────")
item_lookup.append(None) # None means not selectable
for idx, choice in enumerate(group_ascii_choices):
display_items.append(f" {choice}")
item_lookup.append((group, idx))
popup = wx.PopupTransientWindow(self, wx.BORDER_SIMPLE)
panel = wx.Panel(popup)
sizer = wx.BoxSizer(wx.VERTICAL)
listbox = wx.ListBox(panel, choices=ascii_choices, style=wx.LB_SINGLE)
listbox = wx.ListBox(
panel, choices=display_items, style=wx.LB_SINGLE
)
sizer.Add(listbox, 1, wx.EXPAND | wx.ALL, 4)
panel.SetSizerAndFit(sizer)
popup.SetClientSize(panel.GetBestSize())
@@ -431,6 +464,23 @@ class IRCPanel(wx.Panel):
# Keep a reference so the popup isn't GC'd
self._kaomoji_popup = popup
def get_kaomoji_by_index(list_idx):
"""Given listbox index, return the chosen kaomoji str or None."""
lookup = item_lookup[list_idx]
if not lookup:
return None
group, group_idx = lookup
if group:
choices = [c for c in kaomoji_groups[group] if is_ascii(c)]
return choices[group_idx]
else:
# Fallback: old style flat
return display_items[list_idx].strip()
def is_selectable(list_idx):
"""Whether this list index is a real choice (not a separator/label)."""
return item_lookup[list_idx] is not None
def on_select(evt):
"""Handle selection from the kaomoji list (keyboard or programmatic)."""
# Ignore synthetic selection changes triggered only for hover visualization
@@ -444,10 +494,9 @@ class IRCPanel(wx.Panel):
idx = evt.GetInt() if hasattr(evt, "GetInt") else -1
try:
if 0 <= idx < len(ascii_choices):
choice = ascii_choices[idx]
if idx is not None and is_selectable(idx):
choice = get_kaomoji_by_index(idx)
if choice:
# Insert at current caret position, but DO NOT auto-send.
current = self.input_ctrl.GetValue()
pos = self.input_ctrl.GetInsertionPoint()
needs_space = pos > 0 and not current[pos - 1].isspace()
@@ -463,7 +512,7 @@ class IRCPanel(wx.Panel):
try:
pos = evt.GetPosition()
idx = listbox.HitTest(pos)
if idx != wx.NOT_FOUND:
if idx != wx.NOT_FOUND and is_selectable(idx):
# Ensure the item is selected, then reuse on_select logic
listbox.SetSelection(idx)
cmd_evt = wx.CommandEvent(wx.wxEVT_LISTBOX, listbox.GetId())
@@ -481,19 +530,19 @@ class IRCPanel(wx.Panel):
pos = evt.GetPosition()
idx = listbox.HitTest(pos)
current_sel = listbox.GetSelection()
if idx != wx.NOT_FOUND and idx != current_sel:
if idx != wx.NOT_FOUND and is_selectable(idx) and idx != current_sel:
# Temporarily suppress on_select so hover highlight doesn't send
self._suppress_kaomoji_select = True
try:
listbox.SetSelection(idx)
finally:
self._suppress_kaomoji_select = False
elif idx == wx.NOT_FOUND:
# Optionally clear selection when hovering outside items
elif idx == wx.NOT_FOUND or not is_selectable(idx):
# Optionally clear selection when hovering outside items or on label row
self._suppress_kaomoji_select = True
try:
listbox.DeselectAll()
# wx.ListBox does not have DeselectAll. Use SetSelection(-1) to clear selection.
listbox.SetSelection(wx.NOT_FOUND)
finally:
self._suppress_kaomoji_select = False
except Exception as e:
@@ -506,10 +555,13 @@ class IRCPanel(wx.Panel):
listbox.Bind(wx.EVT_LEFT_DOWN, on_left_click)
listbox.Bind(wx.EVT_MOTION, on_motion)
def on_draw_item(event):
pass
# Position popup under the kaomoji button
btn = self.kaomoji_btn
btn_pos = btn.ClientToScreen((0, btn.GetSize().height))
popup.Position(btn_pos, (0, 0))
popup.Popup()
except Exception as e:
logger.error(f"Error in kaomoji picker: {e}")
logger.error(f"Error in kaomoji picker: {e}")