From 4c70953f5cadf346f5b3034294679884ae8f04e7 Mon Sep 17 00:00:00 2001 From: "Mingde (Matthew) Zeng" Date: Fri, 19 Jun 2020 10:46:43 -0400 Subject: [PATCH] Redesign build_interactive_method, generalize with build_all_methods Signed-off-by: Mingde (Matthew) Zeng --- app/mindmap/buffer.py | 26 ++++++------ app/pdf-viewer/buffer.py | 28 ++++++++---- app/rss-reader/buffer.py | 22 +++++----- app/terminal/buffer.py | 14 ++++-- app/video-player/buffer.py | 10 +++-- core/browser.py | 87 ++++++++++++++++++++------------------ core/buffer.py | 42 ++++++++++++------ core/utils.py | 13 ++++++ eaf.el | 6 +-- 9 files changed, 151 insertions(+), 97 deletions(-) diff --git a/app/mindmap/buffer.py b/app/mindmap/buffer.py index 03c4367..97bc7de 100644 --- a/app/mindmap/buffer.py +++ b/app/mindmap/buffer.py @@ -24,7 +24,7 @@ from PyQt5.QtCore import QUrl, QTimer from PyQt5.QtWidgets import QApplication from PyQt5.QtGui import QColor from core.browser import BrowserBuffer -from core.utils import touch, string_to_base64 +from core.utils import touch, string_to_base64, interactive import os import base64 @@ -50,13 +50,13 @@ class AppBuffer(BrowserBuffer): self.build_js_method(method_name) for method_name in ["zoom_in", "zoom_out", "zoom_reset", "remove_node", - "remove_middle_node", "add_middle_node", "update_node_topic", - "copy_node_topic", "paste_node_topic", "refresh_page", + "remove_middle_node", "add_middle_node", "refresh_page", "select_up_node", "select_down_node", "select_left_node", "select_right_node", - "toggle_node", "save_screenshot", "save_file", "save_org_file", - "change_node_background", "cut_node_tree", "paste_node_tree"]: + "toggle_node", "save_screenshot"]: self.build_insert_or_do(method_name) + self.build_all_methods(self) + QTimer.singleShot(500, self.init_file) def resize_view(self): @@ -87,19 +87,13 @@ class AppBuffer(BrowserBuffer): self.save_file(False) setattr(self, method_name, _do) - def build_insert_or_do(self, method_name): - def _do (): - if self.is_focus(): - self.fake_key_event(self.current_event_string) - else: - getattr(self, method_name)() - setattr(self, "insert_or_{}".format(method_name), _do) - + @interactive(insert_or_do=True) def copy_node_topic(self): node_topic = self.buffer_widget.execute_js("get_node_topic();") self.eval_in_emacs.emit('''(kill-new "{}")'''.format(node_topic)) self.message_to_emacs.emit("Copy: {}".format(node_topic)) + @interactive(insert_or_do=True) def paste_node_topic(self): text = QApplication.clipboard().text() if text.strip() != "": @@ -110,6 +104,7 @@ class AppBuffer(BrowserBuffer): else: self.message_to_emacs.emit("Nothing in clipboard, can't paste.") + @interactive(insert_or_do=True) def cut_node_tree(self): self.cut_node_id = self.buffer_widget.execute_js("get_selected_nodeid();") if self.cut_node_id: @@ -118,15 +113,18 @@ class AppBuffer(BrowserBuffer): else: self.message_to_emacs.emit("Cut node tree: {}".format(self.cut_node_id)) + @interactive(insert_or_do=True) def paste_node_tree(self): if self.cut_node_id: self.buffer_widget.eval_js("paste_node_tree('{}');".format(self.cut_node_id)) self.save_file(False) self.message_to_emacs.emit("Paste node tree: {}".format(self.cut_node_id)) + @interactive(insert_or_do=True) def change_node_background(self): self.send_input_message("Change node background: ", "change_node_background", "file") + @interactive(insert_or_do=True) def update_node_topic(self): self.send_input_message( "Update topic: ", @@ -176,6 +174,7 @@ class AppBuffer(BrowserBuffer): self.message_to_emacs.emit("Save image: " + image_path) + @interactive(insert_or_do=True) def save_file(self, notify=True): file_path = self.get_save_path("emm") touch(file_path) @@ -185,6 +184,7 @@ class AppBuffer(BrowserBuffer): if notify: self.message_to_emacs.emit("Save file: " + file_path) + @interactive(insert_or_do=True) def save_org_file(self): file_path = self.get_save_path("org") touch(file_path) diff --git a/app/pdf-viewer/buffer.py b/app/pdf-viewer/buffer.py index 1bb4c47..9582b4d 100755 --- a/app/pdf-viewer/buffer.py +++ b/app/pdf-viewer/buffer.py @@ -25,7 +25,7 @@ from PyQt5.QtGui import QColor, QPixmap, QImage, QFont, QCursor from PyQt5.QtGui import QPainter from PyQt5.QtWidgets import QWidget from core.buffer import Buffer -from core.utils import touch +from core.utils import touch, interactive import fitz import time import random @@ -41,13 +41,7 @@ class AppBuffer(Buffer): self.add_widget(PdfViewerWidget(url, config_dir, QColor(0, 0, 0, 255), buffer_id, emacs_var_dict)) self.buffer_widget.translate_double_click_word.connect(self.translate_text) - for method_name in ["scroll_up", "scroll_down", "scroll_up_page", - "scroll_down_page", "scroll_to_home", "scroll_to_end", - "zoom_reset", "zoom_in", "zoom_out", - "save_current_pos", "jump_to_saved_pos", - "scroll_right", "scroll_left", - "toggle_read_mode", "toggle_inverted_mode", "toggle_mark_link"]: - self.build_interactive_method(method_name, self.buffer_widget) + self.build_all_methods(self.buffer_widget) def destroy_buffer(self): if self.delete_temp_file: @@ -276,10 +270,12 @@ class PdfViewerWidget(QWidget): def repeat_to_length(self, string_to_expand, length): return (string_to_expand * (int(length/len(string_to_expand))+1))[:length] + @interactive() def save_current_pos(self): self.remember_offset = self.scroll_offset self.buffer.message_to_emacs.emit("Saved current position.") + @interactive() def jump_to_saved_pos(self): if self.remember_offset is None: self.buffer.message_to_emacs.emit("Cannot jump from this position.") @@ -489,6 +485,7 @@ class PdfViewerWidget(QWidget): def max_scroll_offset(self): return self.scale * self.page_height * self.page_total_number - self.rect().height() + @interactive() def toggle_read_mode(self): if self.read_mode == "fit_to_customize": self.read_mode = "fit_to_width" @@ -500,32 +497,41 @@ class PdfViewerWidget(QWidget): self.update_scale() self.update() + @interactive() def scroll_up(self): self.update_vertical_offset(min(self.scroll_offset + self.scale * self.scroll_step, self.max_scroll_offset())) + @interactive() def scroll_down(self): self.update_vertical_offset(max(self.scroll_offset - self.scale * self.scroll_step, 0)) + @interactive() def scroll_right(self): self.update_horizontal_offset(max(self.horizontal_offset - self.scale * 30, (self.rect().width() - self.page_width * self.scale) / 2)) + @interactive() def scroll_left(self): self.update_horizontal_offset(min(self.horizontal_offset + (self.scale * 30), (self.page_width * self.scale - self.rect().width()) / 2)) + @interactive() def scroll_up_page(self): # Adjust scroll step to make users continue reading fluently. self.update_vertical_offset(min(self.scroll_offset + self.rect().height() - self.scroll_step, self.max_scroll_offset())) + @interactive() def scroll_down_page(self): # Adjust scroll step to make users continue reading fluently. self.update_vertical_offset(max(self.scroll_offset - self.rect().height() + self.scroll_step, 0)) - def scroll_to_home(self): + @interactive() + def scroll_to_begin(self): self.update_vertical_offset(0) + @interactive() def scroll_to_end(self): self.update_vertical_offset(self.max_scroll_offset()) + @interactive() def zoom_in(self): if self.is_mark_search: self.cleanup_search() @@ -533,6 +539,7 @@ class PdfViewerWidget(QWidget): self.scale_to(min(10, self.scale + 0.2)) self.update() + @interactive() def zoom_out(self): if self.is_mark_search: self.cleanup_search() @@ -540,6 +547,7 @@ class PdfViewerWidget(QWidget): self.scale_to(max(1, self.scale - 0.2)) self.update() + @interactive() def zoom_reset(self): if self.is_mark_search: self.cleanup_search() @@ -547,6 +555,7 @@ class PdfViewerWidget(QWidget): self.update_scale() self.update() + @interactive() def toggle_inverted_mode(self): # Need clear page cache first, otherwise current page will not inverted until next page. self.page_cache_pixmap_dict.clear() @@ -557,6 +566,7 @@ class PdfViewerWidget(QWidget): # Re-render page. self.update() + @interactive() def toggle_mark_link(self): # mark_link will add underline mark on link, using prompt link position. if self.is_mark_link: self.cleanup_mark_link() diff --git a/app/rss-reader/buffer.py b/app/rss-reader/buffer.py index f537c69..597dd2d 100644 --- a/app/rss-reader/buffer.py +++ b/app/rss-reader/buffer.py @@ -25,7 +25,7 @@ from PyQt5.QtWidgets import QPushButton, QHBoxLayout, QWidget, QWidget, QListWid from core.buffer import Buffer from PyQt5 import QtCore from core.browser import BrowserView -from core.utils import touch +from core.utils import touch, interactive import feedparser import json import os @@ -35,16 +35,10 @@ class AppBuffer(Buffer): Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, True) self.add_widget(RSSReaderWidget(config_dir)) + self.buffer_widget.browser.buffer = self - for method_name in ["next_subscription", "prev_subscription", "next_article", "prev_article", - "first_subscription", "last_subscription", "first_article", "last_article"]: - self.build_interactive_method(method_name, self.buffer_widget) - - for method_name in ["scroll_up", "scroll_down", "scroll_up_page", "scroll_down_page", "scroll_to_begin", "scroll_to_bottom", - "search_text_forward", "search_text_backward"]: - self.build_interactive_method(method_name, self.buffer_widget.browser) - - self.build_interactive_method("action_quit", self.buffer_widget.browser, "search_quit") + self.build_all_methods(self.buffer_widget) + self.build_all_methods(self.buffer_widget.browser) def add_subscription(self): self.send_input_message("Subscribe to RSS feed: ", "add_subscription") @@ -372,6 +366,7 @@ class RSSReaderWidget(QWidget): except Exception: pass + @interactive() def next_subscription(self): feed_count = self.feed_list.count() current_row = self.feed_list.currentRow() @@ -383,6 +378,7 @@ class RSSReaderWidget(QWidget): else: self.buffer.message_to_emacs.emit("End of subscribed feeds") + @interactive() def prev_subscription(self): current_row = self.feed_list.currentRow() @@ -393,11 +389,13 @@ class RSSReaderWidget(QWidget): else: self.buffer.message_to_emacs.emit("Beginning of subscribed feeds") + @interactive() def first_subscription(self): self.feed_list.setCurrentRow(0) self.feed_list.scrollToItem(self.feed_list.currentItem()) self.handle_feed(self.feed_list.currentItem()) + @interactive() def last_subscription(self): feed_count = self.feed_list.count() @@ -405,6 +403,7 @@ class RSSReaderWidget(QWidget): self.feed_list.scrollToItem(self.feed_list.currentItem()) self.handle_feed(self.feed_list.currentItem()) + @interactive() def next_article(self): article_count = self.article_list.count() current_row = self.article_list.currentRow() @@ -416,6 +415,7 @@ class RSSReaderWidget(QWidget): else: self.buffer.message_to_emacs.emit("End of articles") + @interactive() def prev_article(self): current_row = self.article_list.currentRow() @@ -426,11 +426,13 @@ class RSSReaderWidget(QWidget): else: self.buffer.message_to_emacs.emit("Beginning of articles") + @interactive() def first_article(self): self.article_list.setCurrentRow(0) self.article_list.scrollToItem(self.article_list.currentItem()) self.handle_article(self.article_list.currentItem()) + @interactive() def last_article(self): article_count = self.article_list.count() diff --git a/app/terminal/buffer.py b/app/terminal/buffer.py index a01acb2..88ba4b4 100644 --- a/app/terminal/buffer.py +++ b/app/terminal/buffer.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import QUrl, QTimer, QEvent, QPointF, Qt from PyQt5.QtGui import QColor, QMouseEvent from PyQt5.QtWidgets import QApplication from core.browser import BrowserBuffer -from core.utils import PostGui, get_free_port +from core.utils import PostGui, get_free_port, interactive import os import subprocess import signal @@ -59,8 +59,7 @@ class AppBuffer(BrowserBuffer): QTimer.singleShot(250, self.focus_terminal) - for method_name in ["search_text_forward", "search_text_backward", "scroll_up", "scroll_down", "scroll_up_page", "scroll_down_page", "scroll_to_begin", "scroll_to_bottom"]: - self.build_interactive_method(method_name, self) + self.build_all_methods(self) def focus_terminal(self): event = QMouseEvent(QEvent.MouseButtonPress, QPointF(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) @@ -125,21 +124,27 @@ class AppBuffer(BrowserBuffer): else: self.scroll_down() + @interactive() def scroll_up(self): self.buffer_widget.eval_js("scroll_line(1);") + @interactive() def scroll_down(self): self.buffer_widget.eval_js("scroll_line(-1);") + @interactive() def scroll_up_page(self): self.buffer_widget.eval_js("scroll_page(1);") + @interactive() def scroll_down_page(self): self.buffer_widget.eval_js("scroll_page(-1);") + @interactive() def scroll_to_begin(self): self.buffer_widget.eval_js("scroll_to_begin();") + @interactive() def scroll_to_bottom(self): self.buffer_widget.eval_js("scroll_to_bottom();") @@ -159,18 +164,21 @@ class AppBuffer(BrowserBuffer): # self.web_page.findText(self.search_term) self.buffer_widget.eval_js("find_prev('{}')".format(text)) + @interactive() def search_text_forward(self): if self.search_term == "": self.buffer.send_input_message("Forward Search Text: ", "search_text_forward") else: self._search_text(self.search_term) + @interactive() def search_text_backward(self): if self.search_term == "": self.buffer.send_input_message("Backward Search Text: ", "search_text_backward") else: self._search_text(self.search_term, True) + @interactive() def search_quit(self): if self.search_term != "": self._search_text("") diff --git a/app/video-player/buffer.py b/app/video-player/buffer.py index 14bc5da..9c7b2d2 100644 --- a/app/video-player/buffer.py +++ b/app/video-player/buffer.py @@ -25,6 +25,7 @@ from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer from PyQt5.QtMultimediaWidgets import QGraphicsVideoItem from PyQt5.QtWidgets import QWidget, QGraphicsScene, QGraphicsView, QVBoxLayout from core.buffer import Buffer +from core.utils import interactive class AppBuffer(Buffer): def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path): @@ -33,8 +34,7 @@ class AppBuffer(Buffer): self.add_widget(VideoPlayerWidget()) self.buffer_widget.play(url) - self.build_interactive_method("play_backward", self.buffer_widget, "seek_backward") - self.build_interactive_method("play_forward", self.buffer_widget, "seek_forward") + self.build_all_methods(self.buffer_widget) def all_views_hide(self): # Pause video before all views hdie, otherwise will got error "Internal data stream error". @@ -99,10 +99,12 @@ class VideoPlayerWidget(QWidget): self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(url))) self.media_player.play() - def seek_forward(self): + @interactive() + def play_forward(self): video_position = self.media_player.position() self.media_player.setPosition(video_position + self.video_seek_durcation) - def seek_backward(self): + @interactive() + def play_backward(self): video_position = self.media_player.position() self.media_player.setPosition(max(video_position - self.video_seek_durcation, 0)) diff --git a/core/browser.py b/core/browser.py index 2215efc..76b8e54 100644 --- a/core/browser.py +++ b/core/browser.py @@ -25,7 +25,7 @@ from PyQt5.QtGui import QBrush, QColor from PyQt5.QtNetwork import QNetworkCookie from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineContextMenuData, QWebEngineProfile, QWebEngineSettings from PyQt5.QtWidgets import QApplication, QWidget -from core.utils import touch, is_port_in_use, string_to_base64, popen_and_call, call_and_check_code +from core.utils import touch, is_port_in_use, string_to_base64, popen_and_call, call_and_check_code, interactive from core.buffer import Buffer from urllib.parse import urlparse, parse_qs, urlunparse, urlencode import os @@ -122,18 +122,21 @@ class BrowserView(QWebEngineView): else: self.web_page.findText(self.search_term) + @interactive() def search_text_forward(self): if self.search_term == "": self.buffer.send_input_message("Forward Search Text: ", "search_text_forward") else: self._search_text(self.search_term) + @interactive() def search_text_backward(self): if self.search_term == "": self.buffer.send_input_message("Backward Search Text: ", "search_text_backward") else: self._search_text(self.search_term, True) + @interactive(new_name = "action_quit") def search_quit(self): if self.search_term != "": self._search_text("") @@ -199,12 +202,15 @@ class BrowserView(QWebEngineView): def open_url_background_buffer(self, url): self.open_url_in_background_tab.emit(url) + @interactive(insert_or_do=True) def zoom_in(self): self.setZoomFactor(min(5, self.zoomFactor() + 0.25)) + @interactive(insert_or_do=True) def zoom_out(self): self.setZoomFactor(max(0.25, self.zoomFactor() - 0.25)) + @interactive(insert_or_do=True) def zoom_reset(self): self.setZoomFactor(float(self.buffer.emacs_var_dict["eaf-browser-default-zoom"])) @@ -217,54 +223,69 @@ class BrowserView(QWebEngineView): def execute_js(self, js): return self.web_page.execute_javascript(js) + @interactive(insert_or_do=True) def scroll_left(self): self.eval_js("document.scrollingElement.scrollBy(-35, 0)") + @interactive(insert_or_do=True) def scroll_right(self): self.eval_js("document.scrollingElement.scrollBy(35, 0)") + @interactive(insert_or_do=True) def scroll_up(self): self.eval_js("document.scrollingElement.scrollBy(0, 50)") + @interactive(insert_or_do=True) def scroll_down(self): self.eval_js("document.scrollingElement.scrollBy(0, -50)") + @interactive(insert_or_do=True) def scroll_up_page(self): self.eval_js("document.scrollingElement.scrollBy({left: 0, top: window.innerHeight/2, behavior: '" + self.buffer.emacs_var_dict["eaf-browser-scroll-behavior"] + "'})") + @interactive(insert_or_do=True) def scroll_down_page(self): self.eval_js("document.scrollingElement.scrollBy({left: 0, top: -window.innerHeight/2, behavior: '" + self.buffer.emacs_var_dict["eaf-browser-scroll-behavior"] + "'})") + @interactive(insert_or_do=True) def scroll_to_begin(self): self.eval_js("document.scrollingElement.scrollTo({left: 0, top: 0, behavior: '" + self.buffer.emacs_var_dict["eaf-browser-scroll-behavior"] + "'})") + @interactive(insert_or_do=True) def scroll_to_bottom(self): self.eval_js("document.scrollingElement.scrollTo({left: 0, top: document.body.scrollHeight, behavior: '" + self.buffer.emacs_var_dict["eaf-browser-scroll-behavior"] + "'})") def get_selection_text(self): return self.execute_js(self.get_selection_text_js) + @interactive(insert_or_do=True) def refresh_page(self): self.reload() def copy_text(self): self.triggerPageAction(self.web_page.Copy) + @interactive(msg_emacs="Yank selected text.") def yank_text(self): self.triggerPageAction(self.web_page.Paste) + @interactive(msg_emacs="Kill selected text.") def kill_text(self): self.triggerPageAction(self.web_page.Cut) + @interactive() def undo_action(self): self.triggerPageAction(self.web_page.Undo) + @interactive() def redo_action(self): self.triggerPageAction(self.web_page.Redo) + @interactive() def exit_fullscreen(self): self.triggerPageAction(self.web_page.ExitFullScreen) + @interactive(insert_or_do=True) def view_source(self): self.triggerPageAction(self.web_page.ViewSource) @@ -276,6 +297,7 @@ class BrowserView(QWebEngineView): def select_input_text(self): self.eval_js(self.select_input_text_js) + @interactive() def get_url(self): return self.execute_js("window.location.href;") @@ -333,16 +355,20 @@ class BrowserView(QWebEngineView): def get_focus_text(self): return self.execute_js(self.get_focus_text_js) + @interactive() def set_focus_text(self, new_text): self.set_focus_text_js = self.set_focus_text_raw.replace("%1", string_to_base64(new_text)); self.eval_js(self.set_focus_text_js) + @interactive() def focus_input(self): self.execute_js(self.focus_input_js) + @interactive() def clear_focus(self): self.eval_js(self.clear_focus_js) + @interactive() def dark_mode(self): self.eval_js(self.dark_mode_js) @@ -466,28 +492,10 @@ class BrowserBuffer(Buffer): except Exception: pass - for method_name in ["search_text_forward", "search_text_backward", "zoom_out", "zoom_in", "zoom_reset", - "scroll_left", "scroll_right", "scroll_up", "scroll_down", - "scroll_up_page", "scroll_down_page", "scroll_to_begin", "scroll_to_bottom", - "refresh_page", "undo_action", "redo_action", "get_url", "exit_fullscreen", - "set_focus_text", "clear_focus", "dark_mode", "view_source", "focus_input"]: - self.build_interactive_method(method_name, self.buffer_widget) - - self.build_interactive_method("history_backward", self.buffer_widget, "back") - self.build_interactive_method("history_forward", self.buffer_widget, "forward") - self.build_interactive_method("action_quit", self.buffer_widget, "search_quit") - self.build_interactive_method("yank_text", self.buffer_widget, "yank_text", "Yank text.") - self.build_interactive_method("kill_text", self.buffer_widget, "kill_text", "Kill text.") - - for method_name in ["recover_prev_close_page", "scroll_up", "scroll_down", "scroll_left", "scroll_right", - "scroll_up_page", "scroll_down_page", "scroll_to_begin", "scroll_to_bottom", - "open_link", "open_link_new_buffer", "open_link_background_buffer", "copy_link", - "history_backward", "history_forward", "new_blank_page", "open_download_manage_page", - "refresh_page", "zoom_in", "zoom_out", "zoom_reset", "save_as_bookmark", "edit_url", - "download_youtube_video", "download_youtube_audio", "toggle_device", "close_buffer", - "save_as_pdf", "view_source", "save_as_single_file", "select_left_tab", "select_right_tab", - "copy_code", "focus_input"]: - self.build_insert_or_do(method_name) + self.build_all_methods(self.buffer_widget) + self.build_all_methods(self) + self.build_interactive_method(self.buffer_widget, "back", "history_backward", insert_or_do=True) + self.build_interactive_method(self.buffer_widget, "forward", "history_forward", insert_or_do=True) def notify_print_message(self, file_path, success): if success: @@ -622,9 +630,11 @@ class BrowserBuffer(Buffer): self.message_to_emacs.emit("Downloading: " + download_url) + @interactive(insert_or_do=True) def save_as_pdf(self): self.send_input_message("Save current webpage as PDF?", "save_as_pdf", "yes-or-no") + @interactive(insert_or_do=True) def save_as_single_file(self): import shutil if shutil.which("monolith") is None: @@ -730,6 +740,7 @@ class BrowserBuffer(Buffer): subprocess.Popen(aria2_args, stdout=null_file) + @interactive(insert_or_do=True) def open_download_manage_page(self): self.try_start_aria2_daemon() self.buffer_widget.open_download_manage_page() @@ -738,22 +749,27 @@ class BrowserBuffer(Buffer): self.buffer_widget.copy_text() self.message_to_emacs.emit("Copy selected text.") + @interactive(insert_or_do=True) def copy_code(self): self.buffer_widget.get_code_markers() self.send_input_message("Copy code: ", "copy_code"); + @interactive(insert_or_do=True) def open_link(self): self.buffer_widget.get_link_markers() self.send_input_message("Open Link: ", "jump_link"); + @interactive(insert_or_do=True) def open_link_new_buffer(self): self.buffer_widget.get_link_markers() self.send_input_message("Open Link in New Buffer: ", "jump_link_new_buffer"); + @interactive(insert_or_do=True) def open_link_background_buffer(self): self.buffer_widget.get_link_markers() self.send_input_message("Open Link in Background Buffer: ", "jump_link_background_buffer"); + @interactive(insert_or_do=True) def copy_link(self): self.buffer_widget.get_link_markers() self.send_input_message("Copy link: ", "copy_link"); @@ -811,6 +827,7 @@ class BrowserBuffer(Buffer): import traceback self.message_to_emacs.emit("Error in record_history: " + str(traceback.print_exc())) + @interactive(insert_or_do=True) def new_blank_page(self): self.eval_in_emacs.emit('''(eaf-open \"{0}\" \"browser\" \"\" t)'''''.format(self.emacs_var_dict["eaf-browser-blank-page-url"])) @@ -827,6 +844,7 @@ class BrowserBuffer(Buffer): with open(self.history_close_file_path, "a") as f: f.write("{0}\n".format(url)) + @interactive(insert_or_do=True) def recover_prev_close_page(self): if os.path.exists(self.history_close_file_path): with open(self.history_close_file_path, "r") as f: @@ -845,24 +863,8 @@ class BrowserBuffer(Buffer): else: self.message_to_emacs.emit("No page need recovery.") - def insert_or_do(func): - def _do(self, *args, **kwargs): - if self.is_focus(): - self.fake_key_event(self.current_event_string) - else: - func(self, *args, **kwargs) - return _do - - def build_insert_or_do(self, method_name): - def _do (): - if self.is_focus(): - self.fake_key_event(self.current_event_string) - else: - getattr(self, method_name)() - setattr(self, "insert_or_{}".format(method_name), _do) - - @insert_or_do - def insert_or_open_url(self): + @interactive(insert_or_do=True) + def open_browser(self): self.eval_in_emacs.emit('''(call-interactively 'eaf-open-browser-with-history)''') def select_all_or_input_text(self): @@ -880,6 +882,7 @@ class BrowserBuffer(Buffer): def open_dev_tool_page(self): self.open_dev_tools_tab.emit(self.buffer_widget.web_page) + @interactive(insert_or_do=True) def toggle_device(self): user_agent = self.profile.defaultProfile().httpUserAgent() if user_agent == self.pc_user_agent: @@ -891,9 +894,11 @@ class BrowserBuffer(Buffer): self.refresh_page() + @interactive(insert_or_do=True) def download_youtube_video(self): self.download_youtube_file() + @interactive(insert_or_do=True) def download_youtube_audio(self): self.download_youtube_file(True) diff --git a/core/buffer.py b/core/buffer.py index 607245e..43ffd72 100755 --- a/core/buffer.py +++ b/core/buffer.py @@ -25,6 +25,7 @@ from PyQt5.QtWidgets import QGraphicsScene from PyQt5.QtCore import Qt, QEvent from PyQt5.QtGui import QKeyEvent from PyQt5.QtWidgets import QApplication +from core.utils import interactive import abc import string @@ -141,6 +142,29 @@ class Buffer(QGraphicsScene): self.enter_fullscreen_request.connect(self.enable_fullscreen) self.exit_fullscreen_request.connect(self.disable_fullscreen) + def build_all_methods(self, origin_class): + method_list = [func for func in dir(origin_class) if callable(getattr(origin_class, func)) and not func.startswith("__")] + for func_name in method_list: + func_attr = getattr(origin_class, func_name) + if hasattr(func_attr, "interactive"): + self.build_interactive_method(origin_class, func_name, getattr(func_attr, "new_name"), getattr(func_attr, "msg_emacs"), getattr(func_attr, "insert_or_do")) + + def build_interactive_method(self, origin_class, class_method_name, new_method_name=None, msg_emacs=None, insert_or_do=False): + new_name = class_method_name if new_method_name is None else new_method_name + self.__dict__.update({new_name: getattr(origin_class, class_method_name)}) + if msg_emacs is not None: + self.message_to_emacs.emit(msg_emacs) + if insert_or_do: + self.build_insert_or_do(new_name) + + def build_insert_or_do(self, method_name): + def _do (): + if self.is_focus(): + self.fake_key_event(self.current_event_string) + else: + getattr(self, method_name)() + setattr(self, "insert_or_{}".format(method_name), _do) + def toggle_fullscreen(self): if self.is_fullscreen: self.exit_fullscreen_request.emit() @@ -173,6 +197,7 @@ class Buffer(QGraphicsScene): self.title = title self.update_buffer_details.emit(self.buffer_id, title, self.url) + @interactive(insert_or_do=True) def close_buffer(self): self.request_close_buffer.emit(self.buffer_id) @@ -280,25 +305,14 @@ class Buffer(QGraphicsScene): def get_url(self): return self.url - def build_interactive_method(self, method_name, origin_method_class, origin_method_name=None, message_emacs=None): - try: - del self.__dict__[method_name] - except KeyError: - pass - finally: - if origin_method_name: - setattr(self, method_name, getattr(origin_method_class, origin_method_name)) - else: - setattr(self, method_name, getattr(origin_method_class, method_name)) - - if message_emacs != None: - self.message_to_emacs.emit(message_emacs) - + @interactive(insert_or_do=True) def save_as_bookmark(self): self.eval_in_emacs.emit('''(bookmark-set)''') + @interactive(insert_or_do=True) def select_left_tab(self): self.goto_left_tab.emit() + @interactive(insert_or_do=True) def select_right_tab(self): self.goto_right_tab.emit() diff --git a/core/utils.py b/core/utils.py index 9661387..34adba4 100644 --- a/core/utils.py +++ b/core/utils.py @@ -27,6 +27,7 @@ import sys import base64 import threading import subprocess +from functools import wraps class PostGui(QtCore.QObject): @@ -126,3 +127,15 @@ def call_and_check_code(popen_args, on_exit, stdout_file=None): thread.start() # returns immediately after the thread starts return thread + +def interactive(insert_or_do = False, msg_emacs = None, new_name = None): + def wrap(f): + f.interactive = True + f.insert_or_do = insert_or_do + f.msg_emacs = msg_emacs + f.new_name = new_name + @wraps(f) + def wrapped_f(*args, **kwargs): + return f(*args, **kwargs) + return wrapped_f + return wrap diff --git a/eaf.el b/eaf.el index 2b68220..d4721aa 100644 --- a/eaf.el +++ b/eaf.el @@ -7,7 +7,7 @@ ;; Copyright (C) 2018, Andy Stewart, all rights reserved. ;; Created: 2018-06-15 14:10:12 ;; Version: 0.5 -;; Last-Updated: Wed Jun 17 03:14:16 2020 (-0400) +;; Last-Updated: Fri Jun 19 03:25:46 2020 (-0400) ;; By: Mingde (Matthew) Zeng ;; URL: http://www.emacswiki.org/emacs/download/eaf.el ;; Keywords: @@ -317,7 +317,7 @@ Try not to modify this alist directly. Use `eaf-setq' to modify instead." ("=" . "insert_or_zoom_in") ("0" . "insert_or_zoom_reset") ("m" . "insert_or_save_as_bookmark") - ("o" . "insert_or_open_url") + ("o" . "insert_or_open_browser") ("y" . "insert_or_download_youtube_video") ("Y" . "insert_or_download_youtube_audio") ("p" . "insert_or_toggle_device") @@ -368,7 +368,7 @@ Try not to modify this alist directly. Use `eaf-setq' to modify instead." ("0" . "zoom_reset") ("=" . "zoom_in") ("-" . "zoom_out") - ("g" . "scroll_to_home") + ("g" . "scroll_to_begin") ("G" . "scroll_to_end") ("p" . "jump_to_page") ("P" . "jump_to_percent")