update to be better ; still have to fix the scrolling on windows
This commit is contained in:
153
src/IRCPanel.py
153
src/IRCPanel.py
@@ -1,4 +1,6 @@
|
||||
import wx , wx.adv
|
||||
import wx
|
||||
import wx.adv
|
||||
import random
|
||||
import threading
|
||||
import logging
|
||||
from SearchDialog import SearchDialog
|
||||
@@ -19,8 +21,7 @@ class IRCPanel(wx.Panel):
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
# Use a better font for chat with white theme
|
||||
self.text_ctrl = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_AUTO_URL)
|
||||
self.text_ctrl = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.TE_AUTO_URL)
|
||||
|
||||
if self.theme:
|
||||
self.text_ctrl.SetBackgroundColour(self.theme["content_bg"])
|
||||
@@ -39,9 +40,44 @@ class IRCPanel(wx.Panel):
|
||||
self.input_ctrl.Bind(wx.EVT_TEXT_ENTER, self.on_send)
|
||||
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_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", ":-|", ":|"
|
||||
]
|
||||
}
|
||||
|
||||
emote_random = random.choice(list(self.kaomoji_groups.values())) # Get a random value ( returns a list and looks like a value of the dict in the code ; )
|
||||
emote_random = str(emote_random).strip('[]').split(', ')[random.randint(0, len(emote_random)-1)].strip("'") # Do some formatting for the strings, since they are stored in lists
|
||||
|
||||
self.kaomoji_btn = wx.Button(self, label=f"{emote_random}")
|
||||
self.kaomoji_btn.SetToolTip(f"Kaomojis {emote_random} ")
|
||||
self.kaomoji_btn.Bind(wx.EVT_BUTTON, self.on_pick_kaomoji)
|
||||
|
||||
send_btn = wx.Button(self, label="Send")
|
||||
@@ -116,7 +152,7 @@ class IRCPanel(wx.Panel):
|
||||
self.target = target
|
||||
|
||||
def add_message(self, message, username=None, username_color=None, message_color=None, bold=False, italic=False, underline=False):
|
||||
"""Thread-safe message addition with username coloring"""
|
||||
"""Thread-safe message addition - plain text only for pixel-perfect scrolling"""
|
||||
try:
|
||||
# Use CallAfter for thread safety
|
||||
if wx.IsMainThread():
|
||||
@@ -127,44 +163,13 @@ class IRCPanel(wx.Panel):
|
||||
logger.error(f"Error in add_message: {e}")
|
||||
|
||||
def _add_message_safe(self, message, username=None, username_color=None, message_color=None, bold=False, italic=False, underline=False):
|
||||
"""Actually add message - must be called from main thread"""
|
||||
"""Actually add message - must be called from main thread
|
||||
Note: Without TE_RICH2, color/formatting is not supported, but scrolling is pixel-perfect"""
|
||||
try:
|
||||
self.messages.append(message)
|
||||
|
||||
# Save current position for formatting
|
||||
start_pos = self.text_ctrl.GetLastPosition()
|
||||
|
||||
if username and username_color:
|
||||
# Add username with its color
|
||||
attr = wx.TextAttr()
|
||||
attr.SetTextColour(username_color)
|
||||
if bold:
|
||||
attr.SetFontWeight(wx.FONTWEIGHT_BOLD)
|
||||
if italic:
|
||||
attr.SetFontStyle(wx.FONTSTYLE_ITALIC)
|
||||
if underline:
|
||||
attr.SetFontUnderlined(True)
|
||||
attr.SetFont(self.font)
|
||||
self.text_ctrl.SetDefaultStyle(attr)
|
||||
self.text_ctrl.AppendText(username)
|
||||
|
||||
# Add the rest of the message with message color
|
||||
attr = wx.TextAttr()
|
||||
if message_color:
|
||||
attr.SetTextColour(message_color)
|
||||
else:
|
||||
attr.SetTextColour(self.default_text_colour)
|
||||
|
||||
attr.SetFont(self.font)
|
||||
self.text_ctrl.SetDefaultStyle(attr)
|
||||
|
||||
# Append the message (without username if we already added it)
|
||||
if username and username_color:
|
||||
# Find the message part after username
|
||||
message_text = message[message.find(username) + len(username):]
|
||||
self.text_ctrl.AppendText(message_text + "\n")
|
||||
else:
|
||||
self.text_ctrl.AppendText(message + "\n")
|
||||
# Simple append without formatting (pixel-perfect scrolling)
|
||||
self.text_ctrl.AppendText(message + "\n")
|
||||
|
||||
# Auto-scroll to bottom
|
||||
self.text_ctrl.ShowPosition(self.text_ctrl.GetLastPosition())
|
||||
@@ -172,23 +177,20 @@ class IRCPanel(wx.Panel):
|
||||
logger.error(f"Error adding message safely: {e}")
|
||||
|
||||
def add_formatted_message(self, timestamp, username, content, username_color=None, is_action=False):
|
||||
"""Add a formatted message with colored username"""
|
||||
"""Add a formatted message (plain text only for pixel-perfect scrolling)"""
|
||||
try:
|
||||
if is_action:
|
||||
message = f"{timestamp}* {username} {content}"
|
||||
self.add_message(message, f"* {username}", username_color, wx.Colour(128, 0, 128), italic=True) # Dark purple for actions
|
||||
else:
|
||||
message = f"{timestamp}<{username}> {content}"
|
||||
self.add_message(message, f"<{username}>", username_color, self.default_text_colour)
|
||||
self.add_message(message)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding formatted message: {e}")
|
||||
|
||||
def add_system_message(self, message, color=None, bold=False):
|
||||
"""Add system message without username coloring"""
|
||||
"""Add system message (plain text only for pixel-perfect scrolling)"""
|
||||
try:
|
||||
if color is None:
|
||||
color = wx.Colour(0, 0, 128) # Dark blue for system messages
|
||||
self.add_message(message, None, None, color, bold, False, False)
|
||||
self.add_message(message)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding system message: {e}")
|
||||
|
||||
@@ -222,20 +224,16 @@ class IRCPanel(wx.Panel):
|
||||
return
|
||||
|
||||
# Prepare search parameters
|
||||
search_flags = 0
|
||||
if not case_sensitive:
|
||||
# For manual search, we'll handle case sensitivity ourselves
|
||||
search_text_lower = search_text.lower()
|
||||
full_text_lower = full_text.lower()
|
||||
|
||||
# Manual search implementation since wx.TextCtrl doesn't have FindText
|
||||
# Manual search implementation
|
||||
pos = 0
|
||||
while pos < len(full_text):
|
||||
if case_sensitive:
|
||||
# Case sensitive search
|
||||
found_pos = full_text.find(search_text, pos)
|
||||
else:
|
||||
# Case insensitive search
|
||||
found_pos = full_text_lower.find(search_text_lower, pos)
|
||||
|
||||
if found_pos == -1:
|
||||
@@ -243,14 +241,13 @@ class IRCPanel(wx.Panel):
|
||||
|
||||
# For whole word search, verify boundaries
|
||||
if whole_word:
|
||||
# Check if it's a whole word
|
||||
is_word_start = (found_pos == 0 or not full_text[found_pos-1].isalnum())
|
||||
is_word_end = (found_pos + len(search_text) >= len(full_text) or
|
||||
not full_text[found_pos + len(search_text)].isalnum())
|
||||
|
||||
if is_word_start and is_word_end:
|
||||
self.search_positions.append(found_pos)
|
||||
pos = found_pos + 1 # Move forward to avoid infinite loop
|
||||
pos = found_pos + 1
|
||||
else:
|
||||
self.search_positions.append(found_pos)
|
||||
pos = found_pos + len(search_text)
|
||||
@@ -400,38 +397,7 @@ class IRCPanel(wx.Panel):
|
||||
Show a grouped kaomoji popup next to the button and insert the chosen one.
|
||||
"""
|
||||
try:
|
||||
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", ":-|", ":|"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
display_items = []
|
||||
item_lookup = [] # tuple: (group, index in group) or None for separators/labels
|
||||
@@ -440,7 +406,7 @@ class IRCPanel(wx.Panel):
|
||||
def is_ascii(s):
|
||||
return all(ord(ch) < 128 for ch in s)
|
||||
|
||||
for group, choices in kaomoji_groups.items():
|
||||
for group, choices in self.kaomoji_groups.items():
|
||||
group_ascii_choices = [c for c in choices if is_ascii(c)]
|
||||
if not group_ascii_choices:
|
||||
continue
|
||||
@@ -451,11 +417,12 @@ class IRCPanel(wx.Panel):
|
||||
display_items.append(f" {choice}")
|
||||
item_lookup.append((group, idx))
|
||||
|
||||
popup = wx.PopupTransientWindow(self, wx.BORDER_SIMPLE)
|
||||
popup = wx.PopupTransientWindow(self, wx.BORDER_SUNKEN)
|
||||
|
||||
panel = wx.Panel(popup)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
listbox = wx.ListBox(
|
||||
panel, choices=display_items, style=wx.LB_SINGLE
|
||||
panel, choices=display_items, style=wx.LB_SINGLE | wx.BORDER_SUNKEN
|
||||
)
|
||||
sizer.Add(listbox, 1, wx.EXPAND | wx.ALL, 4)
|
||||
panel.SetSizerAndFit(sizer)
|
||||
@@ -471,7 +438,7 @@ class IRCPanel(wx.Panel):
|
||||
return None
|
||||
group, group_idx = lookup
|
||||
if group:
|
||||
choices = [c for c in kaomoji_groups[group] if is_ascii(c)]
|
||||
choices = [c for c in self.kaomoji_groups[group] if is_ascii(c)]
|
||||
return choices[group_idx]
|
||||
else:
|
||||
# Fallback: old style flat
|
||||
@@ -505,7 +472,7 @@ class IRCPanel(wx.Panel):
|
||||
self.input_ctrl.SetValue(new_value)
|
||||
self.input_ctrl.SetInsertionPoint(pos + len(insert_text))
|
||||
finally:
|
||||
popup.Dismiss()
|
||||
popup.Destroy() # Linux did not like dismiss, so we just destroy it. it works better
|
||||
|
||||
def on_left_click(evt):
|
||||
"""Single left-click handler for the kaomoji menu."""
|
||||
|
||||
Reference in New Issue
Block a user