aboutsummaryrefslogtreecommitdiff
blob: db8625cff129d524934cf5fd0b33f8193e32404e (plain)
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)