update the kaomoji / emote selector
This commit is contained in:
116
src/IRCPanel.py
116
src/IRCPanel.py
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user