📄 canvas.py
字号:
# top-left-aligned cviews, compare them if cv[5] is other_cv[5] and cv[:5] == other_cv[:5]: yield cv[:5]+(None,)+cv[6:] else: yield cv other_cols += other_cv[2] other_cv = None cols += cv[2]def shard_body(cviews, shard_tail, create_iter=True, iter_default=None): """ Return a list of (done_rows, content_iter, cview) tuples for this shard and shard tail. If a canvas in cviews is None (eg. when unchanged from shard_cviews_delta()) or if create_iter is False then no iterator is created for content_iter. iter_default is the value used for content_iter when no iterator is created. """ col = 0 body = [] # build the next shard tail cviews_iter = iter(cviews) for col_gap, done_rows, content_iter, tail_cview in shard_tail: while col_gap: try: cview = cviews_iter.next() except StopIteration: raise CanvasError("cviews do not fill gaps in" " shard_tail!") (trim_left, trim_top, cols, rows, def_attr, canv) = \ cview[:6] col += cols col_gap -= cols if col_gap < 0: raise CanvasError("cviews overflow gaps in" " shard_tail!") if create_iter and canv: new_iter = canv.content(trim_left, trim_top, cols, rows, def_attr) else: new_iter = iter_default body.append((0, new_iter, cview)) body.append((done_rows, content_iter, tail_cview)) for cview in cviews_iter: (trim_left, trim_top, cols, rows, def_attr, canv) = \ cview[:6] if create_iter and canv: new_iter = canv.content(trim_left, trim_top, cols, rows, def_attr) else: new_iter = iter_default body.append((0, new_iter, cview)) return bodydef shards_trim_top(shards, top): """ Return shards with top rows removed. """ assert top > 0 shard_iter = iter(shards) shard_tail = [] # skip over shards that are completely removed for num_rows, cviews in shard_iter: if top < num_rows: break sbody = shard_body(cviews, shard_tail, False) shard_tail = shard_body_tail(num_rows, sbody) top -= num_rows else: raise CanvasError("tried to trim shards out of existance") sbody = shard_body(cviews, shard_tail, False) shard_tail = shard_body_tail(num_rows, sbody) # trim the top of this shard new_sbody = [] for done_rows, content_iter, cv in sbody: new_sbody.append((0, content_iter, cview_trim_top(cv, done_rows+top))) sbody = new_sbody new_shards = [(num_rows-top, [cv for done_rows, content_iter, cv in sbody])] # write out the rest of the shards new_shards.extend(shard_iter) return new_shardsdef shards_trim_rows(shards, keep_rows): """ Return the topmost keep_rows rows from shards. """ assert keep_rows >= 0, keep_rows shard_tail = [] new_shards = [] done_rows = 0 for num_rows, cviews in shards: if done_rows >= keep_rows: break new_cviews = [] for cv in cviews: if cv[3] + done_rows > keep_rows: new_cviews.append(cview_trim_rows(cv, keep_rows - done_rows)) else: new_cviews.append(cv) if num_rows + done_rows > keep_rows: new_shards.append((keep_rows - done_rows, new_cviews)) else: new_shards.append((num_rows, new_cviews)) done_rows += num_rows return new_shardsdef shards_trim_sides(shards, left, cols): """ Return shards with starting from column left and cols total width. """ assert left >= 0 and cols > 0 shard_tail = [] new_shards = [] right = left + cols for num_rows, cviews in shards: sbody = shard_body(cviews, shard_tail, False) shard_tail = shard_body_tail(num_rows, sbody) new_cviews = [] col = 0 for done_rows, content_iter, cv in sbody: cv_cols = cv[2] next_col = col + cv_cols if done_rows or next_col <= left or col >= right: col = next_col continue if col < left: cv = cview_trim_left(cv, left - col) col = left if next_col > right: cv = cview_trim_cols(cv, right - col) new_cviews.append(cv) col = next_col if not new_cviews: prev_num_rows, prev_cviews = new_shards[-1] new_shards[-1] = (prev_num_rows+num_rows, prev_cviews) else: new_shards.append((num_rows, new_cviews)) return new_shardsdef shards_join(shard_lists): """ Return the result of joining shard lists horizontally. All shards lists must have the same number of rows. """ shards_iters = [iter(sl) for sl in shard_lists] shards_current = [i.next() for i in shards_iters] new_shards = [] while True: new_cviews = [] num_rows = min([r for r,cv in shards_current]) shards_next = [] for rows, cviews in shards_current: if cviews: new_cviews.extend(cviews) shards_next.append((rows - num_rows, None)) shards_current = shards_next new_shards.append((num_rows, new_cviews)) # advance to next shards try: for i in range(len(shards_current)): if shards_current[i][0] > 0: continue shards_current[i] = shards_iters[i].next() except StopIteration: break return new_shardsdef cview_trim_rows(cv, rows): return cv[:3] + (rows,) + cv[4:] def cview_trim_top(cv, trim): return (cv[0], trim + cv[1], cv[2], cv[3] - trim) + cv[4:]def cview_trim_left(cv, trim): return (cv[0] + trim, cv[1], cv[2] - trim,) + cv[3:]def cview_trim_cols(cv, cols): return cv[:2] + (cols,) + cv[3:] def CanvasCombine(l): """Stack canvases in l vertically and return resulting canvas. l -- list of (canvas, position, focus) tuples. position is a value that widget.set_focus will accept, or None if not allowed. focus is True if this canvas is the one that would be in focus if the whole widget is in focus. """ clist = [(CompositeCanvas(c),p,f) for c,p,f in l] combined_canvas = CompositeCanvas() shards = [] children = [] row = 0 focus_index = 0 n = 0 for canv, pos, focus in clist: if focus: focus_index = n children.append((0, row, canv, pos)) shards.extend(canv.shards) combined_canvas.coords.update(canv.translate_coords(0, row)) for shortcut in canv.shortcuts.keys(): combined_canvas.shortcuts[shortcut] = pos row += canv.rows() n += 1 if focus_index: children = [children[focus_index]] + children[:focus_index] + \ children[focus_index+1:] combined_canvas.shards = shards combined_canvas.children = children return combined_canvasdef CanvasOverlay(top_c, bottom_c, left, top): """ Overlay canvas top_c onto bottom_c at position (left, top). """ overlayed_canvas = CompositeCanvas(bottom_c) overlayed_canvas.overlay(top_c, left, top) overlayed_canvas.children = [(left, top, top_c, None), (0, 0, bottom_c, None)] overlayed_canvas.shortcuts = {} # disable background shortcuts for shortcut in top_c.shortcuts.keys(): overlayed_canvas.shortcuts[shortcut]="fg" return overlayed_canvasdef CanvasJoin(l): """ Join canvases in l horizontally. Return result. l -- list of (canvas, position, focus, cols) tuples. position is a value that widget.set_focus will accept, or None if not allowed. focus is True if this canvas is the one that would be in focus if the whole widget is in focus. cols is the number of screen columns that this widget will require, if larger than the actual canvas.cols() value then this widget will be padded on the right. """ l2 = [] focus_item = 0 maxrow = 0 n = 0 for canv, pos, focus, cols in l: rows = canv.rows() pad_right = cols - canv.cols() if focus: focus_item = n if rows > maxrow: maxrow = rows l2.append((canv, pos, pad_right, rows)) n += 1 shard_lists = [] children = [] joined_canvas = CompositeCanvas() col = 0 for canv, pos, pad_right, rows in l2: canv = CompositeCanvas(canv) if pad_right: canv.pad_trim_left_right(0, pad_right) if rows < maxrow: canv.pad_trim_top_bottom(0, maxrow - rows) joined_canvas.coords.update(canv.translate_coords(col, 0)) for shortcut in canv.shortcuts.keys(): joined_canvas.shortcuts[shortcut] = pos shard_lists.append(canv.shards) children.append((col, 0, canv, pos)) col += canv.cols() if focus_item: children = [children[focus_item]] + children[:focus_item] + \ children[focus_item+1:] joined_canvas.shards = shards_join(shard_lists) joined_canvas.children = children return joined_canvasdef apply_text_layout(text, attr, ls, maxcol): utext = type(text)==type(u"") t = [] a = [] c = [] class AttrWalk: pass aw = AttrWalk aw.k = 0 # counter for moving through elements of a aw.off = 0 # current offset into text of attr[ak] def arange( start_offs, end_offs ): """Return an attribute list for the range of text specified.""" if start_offs < aw.off: aw.k = 0 aw.off = 0 o = [] while aw.off < end_offs: if len(attr)<=aw.k: # run out of attributes o.append((None,end_offs-max(start_offs,aw.off))) break at,run = attr[aw.k] if aw.off+run <= start_offs: # move forward through attr to find start_offs aw.k += 1 aw.off += run continue if end_offs <= aw.off+run: o.append((at, end_offs-max(start_offs,aw.off))) break o.append((at, aw.off+run-max(start_offs, aw.off))) aw.k += 1 aw.off += run return o for line_layout in ls: # trim the line to fit within maxcol line_layout = trim_line( line_layout, text, 0, maxcol ) line = [] linea = [] linec = [] def attrrange( start_offs, end_offs, destw ): """ Add attributes based on attributes between start_offs and end_offs. """ if start_offs == end_offs: [(at,run)] = arange(start_offs,end_offs) rle_append_modify( linea, ( at, destw )) return if destw == end_offs-start_offs: for at, run in arange(start_offs,end_offs): rle_append_modify( linea, ( at, run )) return # encoded version has different width o = start_offs for at, run in arange(start_offs, end_offs): if o+run == end_offs: rle_append_modify( linea, ( at, destw )) return tseg = text[o:o+run] tseg, cs = apply_target_encoding( tseg ) segw = rle_len(cs) rle_append_modify( linea, ( at, segw )) o += run destw -= segw for seg in line_layout: #if seg is None: assert 0, ls s = LayoutSegment(seg) if s.end: tseg, cs = apply_target_encoding( text[s.offs:s.end]) line.append(tseg) attrrange(s.offs, s.end, rle_len(cs)) rle_join_modify( linec, cs ) elif s.text: tseg, cs = apply_target_encoding( s.text ) line.append(tseg) attrrange( s.offs, s.offs, len(tseg) ) rle_join_modify( linec, cs ) elif s.offs: if s.sc: line.append(" "*s.sc) attrrange( s.offs, s.offs, s.sc ) else: line.append(" "*s.sc) linea.append((None, s.sc)) linec.append((None, s.sc)) t.append("".join(line)) a.append(linea) c.append(linec) return TextCanvas(t, a, c, maxcol=maxcol)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -