mirror of
https://github.com/markqvist/NomadNet.git
synced 2026-05-27 16:24:11 +00:00
Browser/Conversations: add ability to copy browser URL and conversation message text
This commit is contained in:
@@ -168,6 +168,7 @@ GLYPHS = {
|
||||
("image", "[I]", "\u25a3", "\uf1c5"),
|
||||
("audio", "[~]", "\u266b", "\uf1c7"),
|
||||
("pin", "*", "\u2605", "\uf08d"),
|
||||
("copy", "[C]", "\u29c9", "\uf0c5"),
|
||||
}
|
||||
|
||||
class TextUI:
|
||||
|
||||
@@ -14,6 +14,7 @@ from nomadnet.Directory import DirectoryEntry
|
||||
from nomadnet.vendor.Scrollable import *
|
||||
from nomadnet.util import strip_modifiers
|
||||
from nomadnet.util import sanitize_name
|
||||
from .Helpers import ClickableIcon, osc52_copy
|
||||
|
||||
class BrowserFrame(urwid.Frame):
|
||||
def keypress(self, size, key):
|
||||
@@ -33,6 +34,8 @@ class BrowserFrame(urwid.Frame):
|
||||
self.delegate.save_node_dialog()
|
||||
elif key == "ctrl g":
|
||||
nomadnet.NomadNetworkApp.get_shared_instance().ui.main_display.sub_displays.network_display.toggle_fullscreen()
|
||||
elif key == "ctrl y":
|
||||
self.delegate.copy_url()
|
||||
elif self.focus_position == "body":
|
||||
if key == "down" or key == "up":
|
||||
try:
|
||||
@@ -488,7 +491,15 @@ class Browser:
|
||||
return urwid.AttrMap(widget, "browser_controls")
|
||||
|
||||
def make_control_widget(self):
|
||||
return urwid.AttrMap(urwid.Pile([urwid.Text(self.g["node"]+" "+self.current_url()), urwid.Divider(self.g["divider1"])]), "browser_controls")
|
||||
url_text = urwid.Text(self.g["node"]+" "+self.current_url())
|
||||
copy_glyph = self.g.get("copy", "[C]")
|
||||
copy_icon = ClickableIcon(copy_glyph, on_click=lambda: self.copy_url())
|
||||
copy_width = len(copy_glyph) + 2
|
||||
header_row = urwid.Columns([
|
||||
("weight", 1, url_text),
|
||||
(copy_width, urwid.Padding(copy_icon, left=1, right=1)),
|
||||
])
|
||||
return urwid.AttrMap(urwid.Pile([header_row, urwid.Divider(self.g["divider1"])]), "browser_controls")
|
||||
|
||||
def make_request_failed_widget(self):
|
||||
def back_action(sender):
|
||||
@@ -1067,6 +1078,34 @@ class Browser:
|
||||
self.uncache_page(self.current_url())
|
||||
self.load_page()
|
||||
|
||||
def copy_url(self):
|
||||
url = self.current_url()
|
||||
if not url:
|
||||
return
|
||||
|
||||
if not osc52_copy(url):
|
||||
return
|
||||
|
||||
try:
|
||||
notice = urwid.AttrMap(urwid.Pile([urwid.Divider(self.g["divider1"]), urwid.Text("Copied URL to clipboard")]), "browser_controls")
|
||||
if self.page_background_color != None or self.page_foreground_color != None:
|
||||
style_name = make_style(default_state(fg=self.page_foreground_color, bg=self.page_background_color))
|
||||
notice.set_attr_map({None: style_name})
|
||||
self.browser_footer = notice
|
||||
self.frame.contents["footer"] = (self.browser_footer, self.frame.options())
|
||||
|
||||
def restore_footer(loop, user_data):
|
||||
if self.status == Browser.DONE:
|
||||
self.browser_footer = self.make_status_widget()
|
||||
else:
|
||||
self.browser_footer = urwid.Text("")
|
||||
if self.frame is not None:
|
||||
self.frame.contents["footer"] = (self.browser_footer, self.frame.options())
|
||||
|
||||
self.app.ui.loop.set_alarm_in(2.0, restore_footer)
|
||||
except Exception as e:
|
||||
RNS.log("Could not update footer after URL copy: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
def close_dialogs(self):
|
||||
options = self.delegate.columns.options(urwid.WEIGHT, self.delegate.right_area_width)
|
||||
self.delegate.columns.contents[1] = (self.display_widget, options)
|
||||
|
||||
@@ -20,6 +20,7 @@ from nomadnet.util import sanitize_name
|
||||
from RNS.Utilities.rngit.util import MarkdownToMicron
|
||||
from RNS.Utilities.rngit.highlight import SyntaxHighlighter
|
||||
from .MicronParser import markup_to_attrmaps
|
||||
from .Helpers import ClickableIcon, osc52_copy
|
||||
from nomadnet.util import strip_modifiers, strip_micron, strip_escaped_micron, unescape_micron, strip_non_formatting_tags
|
||||
from nomadnet.ui import THEME_DARK, THEME_LIGHT
|
||||
|
||||
@@ -2235,7 +2236,7 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
added_hashes.append(message_hash)
|
||||
was_loaded = message.loaded
|
||||
try:
|
||||
message_widget = LXMessageWidget(message, theme=self.app.config["textui"]["theme"])
|
||||
message_widget = LXMessageWidget(message, theme=self.app.config["textui"]["theme"], conversation_widget=self)
|
||||
except Exception as e:
|
||||
RNS.log("Skipping message loading for "+str(message.file_path)+" due to error: "+str(e), RNS.LOG_WARNING)
|
||||
message.unload()
|
||||
@@ -2550,9 +2551,10 @@ class ConversationWidget(urwid.WidgetWrap):
|
||||
class LXMessageWidget(urwid.WidgetWrap):
|
||||
mdc = MarkdownToMicron(max_width=80, syntax_highlighter=SyntaxHighlighter(), url_scope=None)
|
||||
|
||||
def __init__(self, message, theme=THEME_DARK):
|
||||
def __init__(self, message, theme=THEME_DARK, conversation_widget=None):
|
||||
app = nomadnet.NomadNetworkApp.get_shared_instance()
|
||||
g = app.ui.glyphs
|
||||
self._conversation_widget = conversation_widget
|
||||
self.timestamp = message.get_timestamp()
|
||||
self.sort_timestamp = message.sort_timestamp
|
||||
self.transfer_done = False
|
||||
@@ -2629,12 +2631,47 @@ class LXMessageWidget(urwid.WidgetWrap):
|
||||
attachment_strings.append(g[atype if atype != "file" else "file"]+" "+aname)
|
||||
title_string += " | " + " ".join(attachment_strings)
|
||||
|
||||
title = urwid.AttrMap(urwid.Text(title_string), header_style)
|
||||
content_text = message.get_content()
|
||||
|
||||
if content_text:
|
||||
copy_glyph = g.get("copy", "[C]")
|
||||
check_glyph = g.get("check", "v").center(len(copy_glyph))
|
||||
copy_icon = ClickableIcon(copy_glyph)
|
||||
|
||||
conv_widget = self._conversation_widget
|
||||
def on_copy_click(icon=copy_icon, content=content_text, cg=copy_glyph, kg=check_glyph, cw=conv_widget):
|
||||
osc52_copy(content)
|
||||
icon.set_text(kg)
|
||||
def _restore(loop, user_data):
|
||||
icon.set_text(cg)
|
||||
try:
|
||||
app.ui.loop.set_alarm_in(2.0, _restore)
|
||||
except Exception:
|
||||
icon.set_text(cg)
|
||||
if cw is not None and cw.frame is not None:
|
||||
def _refocus(loop, user_data):
|
||||
try:
|
||||
cw.frame.focus_position = "footer"
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
app.ui.loop.set_alarm_in(0, _refocus)
|
||||
except Exception:
|
||||
pass
|
||||
copy_icon._on_click = on_copy_click
|
||||
|
||||
copy_width = len(copy_glyph) + 2
|
||||
title_row = urwid.Columns([
|
||||
("weight", 1, urwid.Text(title_string)),
|
||||
(copy_width, urwid.Padding(copy_icon, left=1, right=1)),
|
||||
])
|
||||
title = urwid.AttrMap(title_row, header_style)
|
||||
else:
|
||||
title = urwid.AttrMap(urwid.Text(title_string), header_style)
|
||||
|
||||
self.progress_widget = urwid.Text("")
|
||||
self.progress_attr = urwid.AttrMap(self.progress_widget, "progress_full")
|
||||
|
||||
content_text = message.get_content()
|
||||
content_lines = content_text.split("\n")
|
||||
markdown = renderer == LXMF.RENDERER_MARKDOWN
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import sys
|
||||
import base64
|
||||
import urwid
|
||||
import RNS
|
||||
|
||||
|
||||
def osc52_copy(text):
|
||||
if not text:
|
||||
return False
|
||||
try:
|
||||
encoded = base64.b64encode(text.encode("utf-8")).decode("ascii")
|
||||
sys.stdout.write("\x1b]52;c;" + encoded + "\x07")
|
||||
sys.stdout.flush()
|
||||
return True
|
||||
except Exception as e:
|
||||
RNS.log("Could not emit clipboard escape sequence: "+str(e), RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
|
||||
class ClickableIcon(urwid.Text):
|
||||
_selectable = False
|
||||
|
||||
def __init__(self, text, on_click=None):
|
||||
super().__init__(text)
|
||||
self._on_click = on_click
|
||||
|
||||
def mouse_event(self, size, event, button, x, y, focus):
|
||||
if button == 1 and "press" in event and self._on_click is not None:
|
||||
self._on_click()
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -16,7 +16,7 @@ class NetworkDisplayShortcuts():
|
||||
self.app = app
|
||||
g = app.ui.glyphs
|
||||
|
||||
self.widget = urwid.AttrMap(urwid.Text("[C-l] Nodes/Announces [C-x] Remove [C-w] Disconnect [C-d] Back [C-f] Forward [C-r] Reload [C-u] URL [C-g] Fullscreen [C-s / C-b] Save Node"), "shortcutbar")
|
||||
self.widget = urwid.AttrMap(urwid.Text("[C-l] Nodes/Announces [C-x] Remove [C-w] Disconnect [C-d] Back [C-f] Forward [C-r] Reload [C-u] URL [C-y] Copy [C-g] Fullscreen [C-s / C-b] Save Node"), "shortcutbar")
|
||||
|
||||
class DialogLineBox(urwid.LineBox):
|
||||
def keypress(self, size, key):
|
||||
|
||||
Reference in New Issue
Block a user