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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
from enum import Enum
from pydoc import pager
from curtsies import FullscreenWindow, fmtstr, fsarray
from curtsies.fmtfuncs import invert
from pomu.util.iquery import Prompt, clamp
class Entry:
def __init__(self, item, source, children=None):
self.expanded = False
self._source = source
self.item = item
self.children = children
def toggle(self, idx=0):
if idx == 0:
self.expanded = not self.expanded
else:
child = self.children[idx - 1]
self.children[idx - 1] = (not child[0], child[1])
if self.expanded and not self.children:
self.children = [(False, x) for x in self._source.list_items(self.item[0])]
def get_idx(self, idx):
return ((self, None if idx == 0 or not self.children else self.children[idx-1]))
def __len__(self):
return len(self.children) + 1 if self.expanded else 1
def selected(self):
if self.children:
return [child[1] for child in self.children if child[0]]
return []
class PromptState(Enum):
TOP_PREV=0
TOP_OK=1
LIST=2
class PSPrompt(Prompt):
def __init__(self, source):
super().__init__([])
self.data = source
self.page_count = self.data.page_count()
self.pages = {}
self.state = PromptState.LIST
self.set_page(1)
def results(self):
# (cp, v, overlay, data)
res = []
for k, v in self.pages.items():
for entry in v:
cp = entry.item[0]
for child in entry.selected():
cid, v, overlay = child
data = self.data.get_item(cid)
res.append((cp, v, overlay, data))
return res
def set_page(self, page):
self.idx = 0
self.page = page
if page in self.pages:
self.entries = self.pages[page]
else:
self.entries = [self.process_entry(x) for x in self.data.get_page(page)]
self.pages[page] = self.entries
def render(self):
title = str(self.page) + '//' + str(self.state.value)
if self.page > 1:
title = ('[ ' +
(invert('Prev') if self.state == 0 else ('Prev')) +
' ] ' + title)
if self.page < self.page_count:
title = (title + ' [ ' +
(invert('Next') if self.state == 1 else ('Next')) +
' ]')
title = self.center(title)
bottom = '[ ' + (invert('OK') if self.idx == len(self) else 'OK') + ' ]'
bottom = self.center(bottom)
items = [self.render_entry(e, idx) for idx, e in enumerate(self.lens())]
output = fsarray([title] + items + [bottom])
self.window.render_to_terminal(output)
def _refresh(self):
w, h = self.window.width, self.window.height
output = fsarray([' ' * w] * h)
self.window.render_to_terminal(output)
def render_entry(self, entry, idx):
winw = self.window.width
if entry[1]:
hld, data = entry[1]
stt = '*' if hld else ' '
text = ' [' + (invert(stt) if idx == 0 else stt) + '] '
text += '{}::{}'.format(data[1], data[2])
elif entry[0]:
data = entry[0].item
exp = 'v' if entry[0].expanded else '>'
text = '[' + (invert(exp) if idx == 0 else exp) + '] '
text += data[0] + ' '
strw = fmtstr(text).width
insw = fmtstr(data[1]).width
text += data[1][:winw - strw - 2] + ('..' if insw + strw > winw else data[1])
else:
text = ''
return text
def process_event(self, event):
res = super().process_event(event)
if res:
return res
elif event == '<TAB>':
self.state = PromptState((self.state.value + 1) % 2)
elif event in {'<Ctrl-j>', '<Ctrl-m>'}:
if self.state.value < 2:
return -1
else:
return False
return True
def __len__(self):
return sum(len(y) for y in self.entries)
def center(self, stri):
tw = fmtstr(stri).width
pw = (self.window.width - tw) // 2
return ' ' * pw + stri + ' ' * pw
def clamp(self, x):
return clamp(x, 0, len(self))
def toggle(self):
item, idx = self.get_idx(self.idx)
item[0].toggle(idx)
def preview(self):
target = self.get_target()
if target[1]:
data = self.data.get_item(target[1][1][0])
pager(data)
self._refresh()
self.render()
def lens(self):
h = self.window.height - 2
lst = [self.get_idx(i)[0] for i in range(self.idx, self.clamp(self.idx + h))]
lst += [(None, None)] * clamp(h - len(lst), 0, h)
return lst
def get_target(self):
return self.get_idx(self.idx)[0]
def get_idx(self, idx):
for entry in self.entries:
if len(entry) > idx:
break
idx -= len(entry)
return (entry.get_idx(idx), idx)
def process_entry(self, item):
return Entry(item, self.data)
def run(self):
return super().run(FullscreenWindow, hide_cursor=True)
|