-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugin_base.py
More file actions
54 lines (46 loc) · 2.03 KB
/
Copy pathplugin_base.py
File metadata and controls
54 lines (46 loc) · 2.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
"""Base class for LinuxPop plugins.
A plugin exposes one action: a button in the popup. It declares which
content types it cares about, which icon and label to render, and what
to do when the user clicks it.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Callable, Iterable, Optional
from classifier import ContentType
@dataclass
class Plugin:
name: str
icon: str
tooltip: str
handler: Callable[[str], None]
content_types: Iterable[ContentType] = field(default_factory=tuple)
priority: int = 100
# Optional fine-grained gate: if set, the button is only shown when the
# predicate returns True for the actual selection text. Used to hide e.g.
# 'URL decode' on text that contains no %-escapes. Exceptions in the
# predicate are treated as False (skip the plugin) - predicates run on
# every popup show, so they must be cheap.
predicate: Optional[Callable[[str], bool]] = None
# If True, the button is hidden when the currently-focused widget is
# not editable (read-only PDF viewer, web page body, image viewer).
# The check uses AT-SPI with a WM_CLASS blocklist as fallback. Set on
# Cut / Paste / Backspace / Bold / Italic / Underline - actions that
# have no effect on read-only text and would just confuse the user.
requires_editable: bool = False
def handles(self, content_type: ContentType) -> bool:
if not self.content_types:
return True
return content_type in self.content_types
def matches(self, text: str) -> bool:
"""Return True if the plugin should appear for this specific text.
Called after handles() - content-type filtering is the coarse gate,
the predicate is the fine-grained one. No predicate means 'always
matches'."""
if self.predicate is None:
return True
try:
return bool(self.predicate(text))
except Exception:
return False
def execute(self, text: str) -> None:
self.handler(text)