some cute emotes
This commit is contained in:
139
src/IRCPanel.py
139
src/IRCPanel.py
@@ -39,11 +39,18 @@ class IRCPanel(wx.Panel):
|
|||||||
self.input_ctrl.Bind(wx.EVT_TEXT_ENTER, self.on_send)
|
self.input_ctrl.Bind(wx.EVT_TEXT_ENTER, self.on_send)
|
||||||
self.input_ctrl.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
|
self.input_ctrl.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
|
||||||
|
|
||||||
|
# Kaomoji picker button - inserts plain ASCII kaomojis into the input box
|
||||||
|
self.kaomoji_btn = wx.Button(self, label="Emotes")
|
||||||
|
self.kaomoji_btn.SetToolTip("Emotes :3")
|
||||||
|
self.kaomoji_btn.Bind(wx.EVT_BUTTON, self.on_pick_kaomoji)
|
||||||
|
|
||||||
send_btn = wx.Button(self, label="Send")
|
send_btn = wx.Button(self, label="Send")
|
||||||
send_btn.SetToolTip("Send message (Enter)")
|
send_btn.SetToolTip("Send message (Enter)")
|
||||||
send_btn.Bind(wx.EVT_BUTTON, self.on_send)
|
send_btn.Bind(wx.EVT_BUTTON, self.on_send)
|
||||||
|
|
||||||
|
# Order: input field, kaomoji, send
|
||||||
input_sizer.Add(self.input_ctrl, 1, wx.EXPAND | wx.ALL, 2)
|
input_sizer.Add(self.input_ctrl, 1, wx.EXPAND | wx.ALL, 2)
|
||||||
|
input_sizer.Add(self.kaomoji_btn, 0, wx.ALL, 2)
|
||||||
input_sizer.Add(send_btn, 0, wx.ALL, 2)
|
input_sizer.Add(send_btn, 0, wx.ALL, 2)
|
||||||
|
|
||||||
sizer.Add(input_sizer, 0, wx.EXPAND | wx.ALL, 0)
|
sizer.Add(input_sizer, 0, wx.EXPAND | wx.ALL, 0)
|
||||||
@@ -365,6 +372,7 @@ class IRCPanel(wx.Panel):
|
|||||||
logger.error(f"Error in tab completion: {e}")
|
logger.error(f"Error in tab completion: {e}")
|
||||||
|
|
||||||
def on_send(self, event):
|
def on_send(self, event):
|
||||||
|
"""Send the current input to the active IRC target."""
|
||||||
try:
|
try:
|
||||||
message = self.input_ctrl.GetValue().strip()
|
message = self.input_ctrl.GetValue().strip()
|
||||||
if message and self.target:
|
if message and self.target:
|
||||||
@@ -374,3 +382,134 @@ class IRCPanel(wx.Panel):
|
|||||||
self.input_ctrl.Clear()
|
self.input_ctrl.Clear()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error sending message: {e}")
|
logger.error(f"Error sending message: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def insert_text_at_caret(self, text):
|
||||||
|
"""Insert given text at the current caret position in the input box."""
|
||||||
|
try:
|
||||||
|
current = self.input_ctrl.GetValue()
|
||||||
|
pos = self.input_ctrl.GetInsertionPoint()
|
||||||
|
new_value = current[:pos] + text + current[pos:]
|
||||||
|
self.input_ctrl.SetValue(new_value)
|
||||||
|
self.input_ctrl.SetInsertionPoint(pos + len(text))
|
||||||
|
except Exception as e:
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
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)", "('_')",
|
||||||
|
]
|
||||||
|
|
||||||
|
ascii_choices = [c for c in choices if all(ord(ch) < 128 for ch in c)]
|
||||||
|
if not ascii_choices:
|
||||||
|
ascii_choices = [":)", ":D", ";)", ":P"]
|
||||||
|
|
||||||
|
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)
|
||||||
|
sizer.Add(listbox, 1, wx.EXPAND | wx.ALL, 4)
|
||||||
|
panel.SetSizerAndFit(sizer)
|
||||||
|
popup.SetClientSize(panel.GetBestSize())
|
||||||
|
|
||||||
|
# Keep a reference so the popup isn't GC'd
|
||||||
|
self._kaomoji_popup = popup
|
||||||
|
|
||||||
|
def on_select(evt):
|
||||||
|
"""Handle selection from the kaomoji list (keyboard or programmatic)."""
|
||||||
|
# Ignore synthetic selection changes triggered only for hover visualization
|
||||||
|
if getattr(self, "_suppress_kaomoji_select", False):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
idx = evt.GetSelection()
|
||||||
|
except AttributeError:
|
||||||
|
# Fallback for synthetic events where we used SetInt()
|
||||||
|
idx = evt.GetInt() if hasattr(evt, "GetInt") else -1
|
||||||
|
|
||||||
|
try:
|
||||||
|
if 0 <= idx < len(ascii_choices):
|
||||||
|
choice = ascii_choices[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()
|
||||||
|
insert_text = (" " + choice) if needs_space else choice
|
||||||
|
new_value = current[:pos] + insert_text + current[pos:]
|
||||||
|
self.input_ctrl.SetValue(new_value)
|
||||||
|
self.input_ctrl.SetInsertionPoint(pos + len(insert_text))
|
||||||
|
finally:
|
||||||
|
popup.Dismiss()
|
||||||
|
|
||||||
|
def on_left_click(evt):
|
||||||
|
"""Single left-click handler for the kaomoji menu."""
|
||||||
|
try:
|
||||||
|
pos = evt.GetPosition()
|
||||||
|
idx = listbox.HitTest(pos)
|
||||||
|
if idx != wx.NOT_FOUND:
|
||||||
|
# Ensure the item is selected, then reuse on_select logic
|
||||||
|
listbox.SetSelection(idx)
|
||||||
|
cmd_evt = wx.CommandEvent(wx.wxEVT_LISTBOX, listbox.GetId())
|
||||||
|
cmd_evt.SetEventObject(listbox)
|
||||||
|
cmd_evt.SetInt(idx)
|
||||||
|
on_select(cmd_evt)
|
||||||
|
else:
|
||||||
|
evt.Skip()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in kaomoji left-click handler: {e}")
|
||||||
|
|
||||||
|
def on_motion(evt):
|
||||||
|
"""Visual hover selector so the current row is highlighted."""
|
||||||
|
try:
|
||||||
|
pos = evt.GetPosition()
|
||||||
|
idx = listbox.HitTest(pos)
|
||||||
|
current_sel = listbox.GetSelection()
|
||||||
|
|
||||||
|
if idx != wx.NOT_FOUND 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
|
||||||
|
self._suppress_kaomoji_select = True
|
||||||
|
try:
|
||||||
|
listbox.DeselectAll()
|
||||||
|
finally:
|
||||||
|
self._suppress_kaomoji_select = False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in kaomoji hover handler: {e}")
|
||||||
|
finally:
|
||||||
|
evt.Skip()
|
||||||
|
|
||||||
|
# Single left-click selects and sends; keyboard selection still works
|
||||||
|
listbox.Bind(wx.EVT_LISTBOX, on_select)
|
||||||
|
listbox.Bind(wx.EVT_LEFT_DOWN, on_left_click)
|
||||||
|
listbox.Bind(wx.EVT_MOTION, on_motion)
|
||||||
|
|
||||||
|
# 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}")
|
||||||
@@ -229,24 +229,41 @@ class ScanWizardResultsPage(adv.WizardPageSimple):
|
|||||||
start_callback(params)
|
start_callback(params)
|
||||||
|
|
||||||
def on_scan_progress(self, scanned, total):
|
def on_scan_progress(self, scanned, total):
|
||||||
|
try:
|
||||||
total = max(total, 1)
|
total = max(total, 1)
|
||||||
self.gauge.SetRange(total)
|
self.gauge.SetRange(total)
|
||||||
self.gauge.SetValue(min(scanned, total))
|
self.gauge.SetValue(min(scanned, total))
|
||||||
self.summary.SetLabel(f"Scanning… {scanned}/{total} hosts checked")
|
self.summary.SetLabel(f"Scanning… {scanned}/{total} hosts checked")
|
||||||
|
except RuntimeError:
|
||||||
|
# C++ SHIT
|
||||||
|
logger.debug("Scan progress update after controls destroyed; ignoring")
|
||||||
|
|
||||||
def on_scan_result(self, server_info):
|
def on_scan_result(self, server_info):
|
||||||
|
"""Handle a single discovered server row."""
|
||||||
|
try:
|
||||||
idx = self.results_list.InsertItem(self.results_list.GetItemCount(), server_info["address"])
|
idx = self.results_list.InsertItem(self.results_list.GetItemCount(), server_info["address"])
|
||||||
self.results_list.SetItem(idx, 1, str(server_info["port"]))
|
self.results_list.SetItem(idx, 1, str(server_info["port"]))
|
||||||
self.results_list.SetItem(idx, 2, server_info.get("banner", "IRC server detected"))
|
self.results_list.SetItem(idx, 2, server_info.get("banner", "IRC server detected"))
|
||||||
self.discovered.append(server_info)
|
self.discovered.append(server_info)
|
||||||
self.summary.SetLabel(f"Found {len(self.discovered)} {"server" if self.discovered == 1 else "servers"}")
|
self.summary.SetLabel(
|
||||||
|
f"Found {len(self.discovered)} {'server' if len(self.discovered) == 1 else 'servers'}"
|
||||||
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
logger.debug("Scan result update after controls destroyed; ignoring")
|
||||||
|
|
||||||
def on_scan_complete(self, results):
|
def on_scan_complete(self, results):
|
||||||
|
"""Final scan completion callback."""
|
||||||
|
try:
|
||||||
if results:
|
if results:
|
||||||
self.summary.SetLabel(f"Scan complete : {len(results)} {"server" if len(results) == 1 else "servers"} ready.")
|
self.summary.SetLabel(
|
||||||
|
f"Scan complete : {len(results)} "
|
||||||
|
f"{'server' if len(results) == 1 else 'servers'} ready."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.summary.SetLabel("Scan complete : no IRC servers discovered.")
|
self.summary.SetLabel("Scan complete : no IRC servers discovered.")
|
||||||
self._toggle_buttons()
|
self._toggle_buttons()
|
||||||
|
except RuntimeError:
|
||||||
|
logger.debug("Scan completion update after controls destroyed; ignoring")
|
||||||
|
|
||||||
def on_quick_connect(self, event):
|
def on_quick_connect(self, event):
|
||||||
row = self.results_list.GetFirstSelected()
|
row = self.results_list.GetFirstSelected()
|
||||||
|
|||||||
Reference in New Issue
Block a user