5 Commits
v1 ... v1.1

Author SHA1 Message Date
admin
03b05ecc08 v1.1 2026-05-24 10:54:17 +00:00
admin
34db86fdac ui v1.1 2026-05-24 10:53:42 +00:00
admin
ed74e97693 config v1.1 2026-05-24 10:53:09 +00:00
admin
9eb1d2cc88 app v1.1 2026-05-24 10:52:44 +00:00
admin
9a64fecd3d update v1.1 2026-05-24 10:52:18 +00:00
7 changed files with 507 additions and 365 deletions

View File

@@ -1,10 +1,10 @@
## overlaynote
desktop note overlay
v1.0
v1.1
---
*v1* <br>
<a href="https://git.backend-3.com/admin/overlaynote/releases/download/v1.0/overlaynote-v1.zip" download>download</a>
*v1.1* <br>
<a href="https://git.backend-3.com/admin/overlaynote/releases/download/v1.0/overlaynote-v1.1.zip" download>download</a>
---
<img width="790vw" src="https://git.backend-3.com/admin/i/raw/branch/main/1etagdzc.png" >

View File

@@ -1,34 +1,62 @@
# v3.1.1 settings.py
# v4.0.0 settings.py
import json
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Final
def resolve_app_dir() -> Path:
if getattr(sys, "frozen", False):
return Path(
sys.executable
).resolve().parent
return (
Path(__file__)
.resolve()
.parent
.parent
)
APP_DIR: Final[Path] = (
Path(__file__).resolve().parent.parent
resolve_app_dir()
)
APP_DIR.mkdir(
parents=True,
exist_ok=True,
)
SETTINGS_FILE: Final[Path] = (
APP_DIR / "settings.json"
)
STATE_FILE: Final[Path] = (
APP_DIR / "active.json"
)
MARKDOWN_FILE: Final[Path] = (
APP_DIR / "note.md"
)
DEFAULT_SETTINGS: Final[dict] = {
"active_profile": "default",
"profiles": {
"default": {
"theme": "macos_blue",
"theme": "dark",
"window": {
"width": 260,
"height": 300,
"min_width": 220,
"min_height": 140,
"layout_margin": 4,
"layout_spacing": 4,
"drag_handle_height": 28,
"width": 250,
"height": 260,
"min_width": 200,
"min_height": 100,
"layout_margin": 0,
"layout_spacing": 0,
"drag_handle_height": 0,
"resize_grip_size": 16,
"corner_radius": 16,
"corner_radius": 8,
"startup_stabilize_ms": 80,
},
"appearance": {
@@ -36,10 +64,10 @@ DEFAULT_SETTINGS: Final[dict] = {
"enable_acrylic": True,
"enable_glass_blur": True,
"enable_animations": True,
"enable_shadow": True,
"opacity_active": 0.99,
"enable_shadow": False,
"opacity_active": 0.97,
"opacity_inactive": 0.95,
"opacity_dragging": 0.98,
"opacity_dragging": 0.89,
"focus_animation_ms": 180,
},
"editor": {
@@ -50,7 +78,7 @@ DEFAULT_SETTINGS: Final[dict] = {
"Arial",
"system-ui",
],
"font_size": 15,
"font_size": 13,
"preview_delay_ms": 700,
"save_debounce_ms": 500,
"max_note_size": 2_000_000,
@@ -58,7 +86,7 @@ DEFAULT_SETTINGS: Final[dict] = {
"enable_clickable_links": True,
"enable_antialiasing": True,
"enable_spellcheck": False,
"padding": 8,
"padding": 0,
},
"behavior": {
"enable_tray": True,
@@ -68,127 +96,49 @@ DEFAULT_SETTINGS: Final[dict] = {
"high_contrast_inactive": False,
},
},
"light": {
"theme": "light",
"window": {
"width": 260,
"height": 300,
"min_width": 220,
"min_height": 140,
"layout_margin": 4,
"layout_spacing": 4,
"drag_handle_height": 28,
"resize_grip_size": 16,
"corner_radius": 16,
"startup_stabilize_ms": 80,
},
"appearance": {
"enable_mica": True,
"enable_acrylic": True,
"enable_glass_blur": True,
"enable_animations": True,
"enable_shadow": False,
"opacity_active": 0.99,
"opacity_inactive": 0.95,
"opacity_dragging": 0.98,
"focus_animation_ms": 120,
},
"editor": {
"font_family": [
"Inter",
"Segoe UI",
"Arial",
"system-ui",
],
"font_size": 13,
"preview_delay_ms": 500,
"save_debounce_ms": 400,
"max_note_size": 2_000_000,
"enable_markdown_preview": True,
"enable_clickable_links": True,
"enable_antialiasing": True,
"enable_spellcheck": False,
"padding": 8,
},
"behavior": {
"enable_tray": True,
"enable_persistence": True,
"enable_window_memory": True,
"enable_auto_restore": True,
"high_contrast_inactive": False,
},
},
"accessibility": {
"theme": "dark",
"window": {
"width": 320,
"height": 380,
"min_width": 260,
"min_height": 180,
"layout_margin": 6,
"layout_spacing": 6,
"drag_handle_height": 32,
"resize_grip_size": 18,
"corner_radius": 18,
"startup_stabilize_ms": 80,
},
"appearance": {
"enable_mica": True,
"enable_acrylic": True,
"enable_glass_blur": True,
"enable_animations": False,
"enable_shadow": True,
"opacity_active": 0.92,
"opacity_inactive": 0.98,
"opacity_dragging": 1.0,
"focus_animation_ms": 0,
},
"editor": {
"font_family": [
"Inter",
"Segoe UI",
"Arial",
"system-ui",
],
"font_size": 15,
"preview_delay_ms": 300,
"save_debounce_ms": 300,
"max_note_size": 2_000_000,
"enable_markdown_preview": True,
"enable_clickable_links": True,
"enable_antialiasing": True,
"enable_spellcheck": False,
"padding": 6,
},
"behavior": {
"enable_tray": True,
"enable_persistence": True,
"enable_window_memory": True,
"enable_auto_restore": True,
"high_contrast_inactive": True,
},
},
},
}
def atomic_write(
path: Path,
content: str,
) -> None:
path.parent.mkdir(
parents=True,
exist_ok=True,
)
temp = path.with_suffix(
f"{path.suffix}.tmp"
)
temp.write_text(
content,
encoding="utf-8",
)
temp.replace(path)
@dataclass(slots=True)
class SettingsManager:
settings: dict
@classmethod
def load(cls) -> "SettingsManager":
SETTINGS_FILE.parent.mkdir(
parents=True,
exist_ok=True,
)
if not SETTINGS_FILE.exists():
SETTINGS_FILE.write_text(
atomic_write(
SETTINGS_FILE,
json.dumps(
DEFAULT_SETTINGS,
indent=2,
),
encoding="utf-8",
)
return cls(
DEFAULT_SETTINGS
)
try:
@@ -203,9 +153,21 @@ class SettingsManager:
data,
)
return cls(merged)
manager = cls(merged)
manager.save()
return manager
except Exception:
atomic_write(
SETTINGS_FILE,
json.dumps(
DEFAULT_SETTINGS,
indent=2,
),
)
return cls(
DEFAULT_SETTINGS
)
@@ -245,12 +207,12 @@ class SettingsManager:
return result
def save(self) -> None:
SETTINGS_FILE.write_text(
atomic_write(
SETTINGS_FILE,
json.dumps(
self.settings,
indent=2,
),
encoding="utf-8",
)
@property
@@ -284,22 +246,6 @@ class SettingsManager:
return profiles[active]
def set_active_profile(
self,
profile_name: str,
) -> None:
if (
profile_name
not in self.profiles
):
return
self.settings[
"active_profile"
] = profile_name
self.save()
def reload(self) -> None:
loaded = (
SettingsManager.load()
@@ -314,44 +260,4 @@ class SettingsManager:
DEFAULT_SETTINGS
)
self.save()
def create_profile(
self,
name: str,
data: dict,
) -> None:
self.settings[
"profiles"
][name] = data
self.save()
def delete_profile(
self,
name: str,
) -> None:
if name == "default":
return
if (
name
not in self.settings[
"profiles"
]
):
return
del self.settings[
"profiles"
][name]
if (
self.active_profile_name
== name
):
self.settings[
"active_profile"
] = "default"
self.save()

View File

@@ -1,18 +1,15 @@
# v3.1.0 app.py
# v4.2.1 app.py
import atexit
import signal
import traceback
import contextlib
import json
import logging
import os
import signal
import subprocess
import sys
import traceback
from dataclasses import dataclass
from enum import Enum, auto
from hashlib import sha256
from pathlib import Path
from typing import Final
from PyQt6.QtCore import (
QEasingCurve,
@@ -49,10 +46,16 @@ from PyQt6.QtWidgets import (
from qframelesswindow import AcrylicWindow
from config.profiles import THEMES
from config.settings import (
APP_DIR,
MARKDOWN_FILE,
SETTINGS_FILE,
STATE_FILE,
SettingsManager,
atomic_write,
)
from ui.editor import LinkTextEdit
from ui.styles import build_stylesheet
@@ -63,15 +66,6 @@ logging.basicConfig(
)
STATE_FILE: Final[Path] = (
APP_DIR / "sticky_state.json"
)
MARKDOWN_FILE: Final[Path] = (
APP_DIR / "sticky.md"
)
class WindowState(Enum):
IDLE = auto()
DRAGGING = auto()
@@ -87,9 +81,8 @@ class ActiveState(Enum):
class PersistedState:
x: int = 100
y: int = 100
width: int = 260
height: int = 300
theme_index: int = 0
width: int = 250
height: int = 260
class DragHandle(QFrame):
@@ -141,15 +134,16 @@ class StickyNoteApp(AcrylicWindow):
def __init__(self):
super().__init__()
self._is_quitting = False
self._force_close = False
self._closing = False
self.settings_manager = (
SettingsManager.load()
)
self.ensure_runtime_files()
self.profile = (
self.settings_manager.profile
)
@@ -164,6 +158,10 @@ class StickyNoteApp(AcrylicWindow):
self.drag_offset = QPoint()
self.drag_origin = QPoint()
self.pending_drag = False
self.last_saved_hash = ""
self.last_preview_hash = ""
@@ -172,12 +170,17 @@ class StickyNoteApp(AcrylicWindow):
self.configure_font()
self.build_ui()
self.setup_timers()
self.build_ui()
self.configure_window()
QTimer.singleShot(
80,
self.restore_state,
)
QTimer.singleShot(
120,
self.initialize_effects,
@@ -188,11 +191,6 @@ class StickyNoteApp(AcrylicWindow):
self.build_tray,
)
QTimer.singleShot(
80,
self.restore_state,
)
QTimer.singleShot(
260,
self.apply_style,
@@ -205,6 +203,34 @@ class StickyNoteApp(AcrylicWindow):
self.register_shutdown_hooks()
def ensure_runtime_files(self) -> None:
APP_DIR.mkdir(
parents=True,
exist_ok=True,
)
if not SETTINGS_FILE.exists():
self.settings_manager.save()
if not MARKDOWN_FILE.exists():
atomic_write(
MARKDOWN_FILE,
"",
)
if not STATE_FILE.exists():
atomic_write(
STATE_FILE,
json.dumps(
{
"x": 100,
"y": 100,
"width": 250,
"height": 260,
},
indent=2,
),
)
def configure_font(self) -> None:
self.font_object = QFont()
@@ -253,6 +279,16 @@ class StickyNoteApp(AcrylicWindow):
self.show_preview
)
self.drag_timer = QTimer(self)
self.drag_timer.setSingleShot(
True
)
self.drag_timer.timeout.connect(
self.activate_delayed_drag
)
self.opacity_anim = (
QPropertyAnimation(
self,
@@ -270,7 +306,6 @@ class StickyNoteApp(AcrylicWindow):
QEasingCurve.Type.OutCubic
)
# v1.0.1 configure_window.py
def configure_window(self) -> None:
window = self.profile["window"]
@@ -289,6 +324,11 @@ class StickyNoteApp(AcrylicWindow):
True,
)
self.setWindowFlag(
Qt.WindowType.FramelessWindowHint,
True,
)
self.setAttribute(
Qt.WidgetAttribute.WA_TranslucentBackground,
True,
@@ -298,19 +338,11 @@ class StickyNoteApp(AcrylicWindow):
Qt.WidgetAttribute.WA_NoSystemBackground,
True,
)
self.setWindowFlag(
Qt.WindowType.WindowCloseButtonHint,
False,
)
self.setAutoFillBackground(
False
)
QTimer.singleShot(
120,
self.initialize_effects,
)
def build_ui(self) -> None:
self.root = QWidget(self)
@@ -318,48 +350,34 @@ class StickyNoteApp(AcrylicWindow):
"container"
)
self.root.setGeometry(
self.rect()
)
self.layout = QVBoxLayout(
self.root
)
window = self.profile["window"]
margin = window[
"layout_margin"
]
self.layout.setContentsMargins(
margin,
margin,
margin,
margin,
4,
4,
4,
4,
)
self.layout.setSpacing(
window["layout_spacing"]
)
self.layout.setSpacing(0)
self.titleBar.hide()
self.titleBar.setFixedHeight(0)
self.drag_handle = DragHandle(
self.root
)
self.drag_handle.setFixedHeight(
window[
"drag_handle_height"
]
)
self.drag_handle.hide()
self.drag_handle.drag_started.connect(
self.start_drag
)
self.drag_handle.drag_moved.connect(
self.perform_drag
)
self.drag_handle.drag_finished.connect(
self.finish_drag
)
self.drag_handle.setFixedHeight(0)
self.editor = LinkTextEdit(
self.root
@@ -377,6 +395,37 @@ class StickyNoteApp(AcrylicWindow):
0
)
self.editor.setViewportMargins(
0,
0,
0,
0,
)
self.editor.setAcceptRichText(
False
)
self.editor.setMouseTracking(
True
)
self.editor.setStyleSheet("""
QTextEdit {
margin: 0px;
padding: 0px;
border: none;
background: transparent;
}
QTextEdit > QWidget {
margin: 0px;
padding: 0px;
border: none;
background: transparent;
}
""")
self.editor.textChanged.connect(
self.queue_save
)
@@ -406,13 +455,13 @@ class StickyNoteApp(AcrylicWindow):
)
content_layout.setContentsMargins(
0,
0,
0,
0,
4,
4,
4,
4,
)
content_layout.setSpacing(0)
content_layout.setSpacing(1)
content_layout.addWidget(
self.editor,
@@ -428,10 +477,6 @@ class StickyNoteApp(AcrylicWindow):
self.root
)
self.layout.addWidget(
self.drag_handle
)
self.layout.addWidget(
content,
1,
@@ -444,12 +489,12 @@ class StickyNoteApp(AcrylicWindow):
| Qt.AlignmentFlag.AlignRight,
)
self.titleBar.hide()
self.editor.setFocus()
def build_tray(self) -> None:
if QSystemTrayIcon.isSystemTrayAvailable() is False:
if not QSystemTrayIcon.isSystemTrayAvailable():
return
if not self.profile[
"behavior"
]["enable_tray"]:
@@ -603,6 +648,7 @@ class StickyNoteApp(AcrylicWindow):
],
)
)
def initialize_effects(self) -> None:
appearance = (
self.profile["appearance"]
@@ -631,16 +677,18 @@ class StickyNoteApp(AcrylicWindow):
)
self.update()
self.repaint()
except Exception:
logging.exception(
"window_effect_failure"
)
def start_drag(
self,
global_pos: QPoint,
) -> None:
def activate_delayed_drag(self) -> None:
if not self.pending_drag:
return
self.window_state = (
WindowState.DRAGGING
)
@@ -651,27 +699,84 @@ class StickyNoteApp(AcrylicWindow):
self.apply_style()
self.drag_offset = (
global_pos
- self.frameGeometry().topLeft()
)
self.setWindowOpacity(
self.profile["appearance"][
"opacity_dragging"
]
)
def perform_drag(
self,
global_pos: QPoint,
) -> None:
target = (
global_pos
- self.drag_offset
def mousePressEvent(self, event) -> None:
if (
event.button()
== Qt.MouseButton.LeftButton
):
self.pending_drag = True
self.drag_origin = (
event.globalPosition()
.toPoint()
)
self.drag_offset = (
self.drag_origin
- self.frameGeometry().topLeft()
)
if hasattr(
self,
"drag_timer",
):
self.drag_timer.start(180)
super().mousePressEvent(event)
def mouseMoveEvent(self, event) -> None:
if not (
event.buttons()
& Qt.MouseButton.LeftButton
):
return
global_pos = (
event.globalPosition()
.toPoint()
)
self.move(target)
if (
self.pending_drag
and (
global_pos
- self.drag_origin
).manhattanLength()
> 6
):
if (
self.window_state
== WindowState.DRAGGING
):
self.move(
global_pos
- self.drag_offset
)
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event) -> None:
if hasattr(
self,
"drag_timer",
):
self.drag_timer.stop()
self.pending_drag = False
if (
self.window_state
== WindowState.DRAGGING
):
self.finish_drag()
super().mouseReleaseEvent(event)
def finish_drag(self) -> None:
self.window_state = (
@@ -690,7 +795,15 @@ class StickyNoteApp(AcrylicWindow):
]
)
self.queue_save()
def queue_save(self) -> None:
if not hasattr(
self,
"save_timer",
):
return
self.save_timer.start(
self.profile["editor"][
"save_debounce_ms"
@@ -698,6 +811,12 @@ class StickyNoteApp(AcrylicWindow):
)
def queue_preview(self) -> None:
if not hasattr(
self,
"preview_timer",
):
return
if not self.profile[
"editor"
][
@@ -721,9 +840,11 @@ class StickyNoteApp(AcrylicWindow):
or not self.isActiveWindow()
):
return
self.preview.setFocusPolicy(
Qt.FocusPolicy.NoFocus
)
markdown = (
self.editor.toPlainText()
)
@@ -751,49 +872,65 @@ class StickyNoteApp(AcrylicWindow):
self.preview.show()
def save_state(self) -> None:
markdown = (
self.editor.toPlainText()
)
current_hash = sha256(
markdown.encode("utf-8")
).hexdigest()
if current_hash != self.last_saved_hash:
MARKDOWN_FILE.write_text(
markdown,
encoding="utf-8",
try:
APP_DIR.mkdir(
parents=True,
exist_ok=True,
)
self.last_saved_hash = (
markdown = (
self.editor.toPlainText()
)
current_hash = sha256(
markdown.encode("utf-8")
).hexdigest()
if (
current_hash
!= self.last_saved_hash
):
atomic_write(
MARKDOWN_FILE,
markdown,
)
self.last_saved_hash = (
current_hash
)
atomic_write(
STATE_FILE,
json.dumps(
{
"x": self.x(),
"y": self.y(),
"width": self.width(),
"height": self.height(),
},
indent=2,
),
)
STATE_FILE.write_text(
json.dumps(
{
"x": self.x(),
"y": self.y(),
"width": self.width(),
"height": self.height(),
},
indent=2,
),
encoding="utf-8",
)
except Exception:
logging.exception(
"save_state_failure"
)
def restore_state(self) -> None:
if MARKDOWN_FILE.exists():
self.editor.setPlainText(
try:
self.ensure_runtime_files()
markdown = (
MARKDOWN_FILE.read_text(
encoding="utf-8",
)
)
if not STATE_FILE.exists():
return
self.editor.setPlainText(
markdown
)
try:
state = json.loads(
STATE_FILE.read_text(
encoding="utf-8",
@@ -801,13 +938,25 @@ class StickyNoteApp(AcrylicWindow):
)
self.resize(
state["width"],
state["height"],
max(
state.get(
"width",
250,
),
200,
),
max(
state.get(
"height",
260,
),
100,
),
)
self.move(
state["x"],
state["y"],
state.get("x", 100),
state.get("y", 100),
)
self.clamp_to_screen()
@@ -867,29 +1016,38 @@ class StickyNoteApp(AcrylicWindow):
self.apply_style()
def open_settings(self) -> None:
path = (
APP_DIR / "settings.json"
SETTINGS_FILE.touch(
exist_ok=True
)
path.touch(exist_ok=True)
try:
if os.name == "nt":
os.startfile(str(path))
os.startfile(
str(SETTINGS_FILE)
)
return
if sys.platform.startswith(
"linux"
):
subprocess.Popen(
["xdg-open", str(path)]
[
"xdg-open",
str(SETTINGS_FILE),
]
)
return
if sys.platform == "darwin":
subprocess.Popen(
["open", str(path)]
[
"open",
str(SETTINGS_FILE),
]
)
return
except Exception:
@@ -897,18 +1055,21 @@ class StickyNoteApp(AcrylicWindow):
"open_settings_failure"
)
def nativeEvent(self, eventType, message):
try:
return super().nativeEvent(eventType, message)
except KeyboardInterrupt:
self.safe_exit()
return False, 0
except SystemExit:
self.safe_exit()
return False, 0
except Exception:
traceback.print_exc()
return False, 0
def moveEvent(self, event) -> None:
super().moveEvent(event)
if (
getattr(
self,
"startup_ready",
False,
)
and hasattr(
self,
"save_timer",
)
):
self.queue_save()
def resizeEvent(self, event) -> None:
super().resizeEvent(event)
@@ -919,15 +1080,29 @@ class StickyNoteApp(AcrylicWindow):
None,
)
if root is None:
return
if root is not None:
geometry = self.rect()
geometry = self.rect()
if (
root.geometry()
!= geometry
):
root.setGeometry(
geometry
)
if root.geometry() != geometry:
root.setGeometry(
geometry
if (
getattr(
self,
"startup_ready",
False,
)
and hasattr(
self,
"save_timer",
)
):
self.queue_save()
def focusInEvent(self, event) -> None:
super().focusInEvent(event)
@@ -966,16 +1141,21 @@ class StickyNoteApp(AcrylicWindow):
)
def closeEvent(self, event) -> None:
if self._is_quitting or self._force_close:
if (
self._is_quitting
or self._force_close
):
try:
self.save_state()
except Exception:
traceback.print_exc()
event.accept()
return
event.ignore()
self.hide()
def register_shutdown_hooks(self) -> None:
@@ -984,11 +1164,20 @@ class StickyNoteApp(AcrylicWindow):
def handle_shutdown(*_) -> None:
self.safe_exit()
atexit.register(handle_shutdown)
atexit.register(
handle_shutdown
)
for sig in (signal.SIGINT, signal.SIGTERM):
for sig in (
signal.SIGINT,
signal.SIGTERM,
):
try:
signal.signal(sig, handle_shutdown)
signal.signal(
sig,
handle_shutdown,
)
except Exception:
pass
@@ -1013,24 +1202,38 @@ class StickyNoteApp(AcrylicWindow):
traceback.print_exc()
try:
if hasattr(self, "save_timer"):
if hasattr(
self,
"save_timer",
):
self.save_timer.stop()
if hasattr(self, "preview_timer"):
if hasattr(
self,
"preview_timer",
):
self.preview_timer.stop()
except Exception:
traceback.print_exc()
try:
if hasattr(self, "tray"):
if hasattr(
self,
"tray",
):
self.tray.hide()
self.tray.deleteLater()
except Exception:
traceback.print_exc()
try:
self.hide()
self.deleteLater()
except Exception:
traceback.print_exc()
@@ -1039,7 +1242,7 @@ class StickyNoteApp(AcrylicWindow):
if app is not None:
app.quit()
try:
app.quit()
except:
sys.exit(0)
try:
app.quit()
except Exception:
sys.exit(0)

View File

@@ -1,31 +1,45 @@
# v1.0.3 start.py
import os
os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1"
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
import signal
import sys
import traceback
os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1"
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QGuiApplication
from PyQt6.QtWidgets import QApplication
from config.settings import (
APP_DIR,
)
from core.app import StickyNoteApp
APP_DIR.mkdir(
parents=True,
exist_ok=True,
)
QGuiApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)
def main() -> int:
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(
signal.SIGINT,
signal.SIG_DFL,
)
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(
False
)
window = StickyNoteApp()
window.show()
try:

BIN
ui/app.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,10 +1,19 @@
# v3.0.0 editor.py
# v4.1.0 editor-link-click-fix-v1
import re
import webbrowser
from PyQt6.QtCore import Qt, QUrl
from PyQt6.QtGui import QDesktopServices
from PyQt6.QtWidgets import QTextEdit
from PyQt6.QtCore import (
Qt,
QUrl,
)
from PyQt6.QtGui import (
QDesktopServices,
)
from PyQt6.QtWidgets import (
QTextEdit,
)
URL_PATTERN = re.compile(
@@ -15,9 +24,14 @@ URL_PATTERN = re.compile(
class LinkTextEdit(QTextEdit):
def mouseReleaseEvent(self, event) -> None:
def mouseReleaseEvent(
self,
event,
) -> None:
if (
event.modifiers()
event.button()
== Qt.MouseButton.LeftButton
and event.modifiers()
& Qt.KeyboardModifier.ControlModifier
):
cursor = self.cursorForPosition(
@@ -47,14 +61,19 @@ class LinkTextEdit(QTextEdit):
)
try:
webbrowser.open(target)
webbrowser.open(
target
)
except Exception:
QDesktopServices.openUrl(
QUrl(target)
)
event.accept()
return
super().mouseReleaseEvent(event)
super().mouseReleaseEvent(
event
)

View File

@@ -28,7 +28,7 @@ def build_stylesheet(
QWidget#container {{
background: {background};
border: 1px solid {border};
border-radius: 16px;
border-radius: 7px;
}}
QWidget#container:hover {{