📄 widget.py
字号:
self.caption, self.attrib = decompose_tagmarkup(caption) self._invalidate() def set_edit_pos(self, pos): """Set the cursor position with a self.edit_text offset.""" assert pos >= 0 and pos <= len(self.edit_text), "out of range" self.highlight = None self.pref_col_maxcol = None, None self.edit_pos = pos self._invalidate() def set_edit_text(self, text): """Set the edit text for this widget.""" self.highlight = None self.edit_text = text if self.edit_pos > len(text): self.edit_pos = len(text) self._invalidate() def get_edit_text(self): """Return the edit text for this widget.""" return self.edit_text def insert_text(self, text): """Insert text at the cursor position and update cursor.""" p = self.edit_pos self.set_edit_text( self.edit_text[:p] + text + self.edit_text[p:] ) self.set_edit_pos( self.edit_pos + len(text)) def keypress(self,(maxcol,),key): """Handle editing keystrokes, return others.""" p = self.edit_pos if self.valid_char(key): self._delete_highlighted() if type(key) == type(u""): key = key.encode("utf-8") self.insert_text( key ) elif key=="tab" and self.allow_tab: self._delete_highlighted() key = " "*(8-(self.edit_pos%8)) self.insert_text( key ) elif key=="enter" and self.multiline: self._delete_highlighted() key = "\n" self.insert_text( key ) elif key=="left": if p==0: return key p = move_prev_char(self.edit_text,0,p) self.set_edit_pos(p) elif key=="right": if p >= len(self.edit_text): return key p = move_next_char(self.edit_text,p,len(self.edit_text)) self.set_edit_pos(p) elif key in ("up","down"): self.highlight = None x,y = self.get_cursor_coords((maxcol,)) pref_col = self.get_pref_col((maxcol,)) assert pref_col is not None #if pref_col is None: # pref_col = x if key == "up": y -= 1 else: y += 1 if not self.move_cursor_to_coords((maxcol,),pref_col,y): return key elif key=="backspace": self._delete_highlighted() self.pref_col_maxcol = None, None if p == 0: return key p = move_prev_char(self.edit_text,0,p) self.set_edit_text( self.edit_text[:p] + self.edit_text[self.edit_pos:] ) self.set_edit_pos( p ) elif key=="delete": self._delete_highlighted() self.pref_col_maxcol = None, None if p >= len(self.edit_text): return key p = move_next_char(self.edit_text,p,len(self.edit_text)) self.set_edit_text( self.edit_text[:self.edit_pos] + self.edit_text[p:] ) elif key in ("home", "end"): self.highlight = None self.pref_col_maxcol = None, None x,y = self.get_cursor_coords((maxcol,)) if key == "home": self.move_cursor_to_coords((maxcol,),'left',y) else: self.move_cursor_to_coords((maxcol,),'right',y) return else: # key wasn't handled return key def move_cursor_to_coords(self, (maxcol,), x, y): """Set the cursor position with (x,y) coordinates. Returns True if move succeeded, False otherwise. """ trans = self.get_line_translation(maxcol) top_x, top_y = self.position_coords(maxcol, 0) if y < top_y or y >= len(trans): return False pos = calc_pos( self.get_text()[0], trans, x, y ) e_pos = pos - len(self.caption) if e_pos < 0: e_pos = 0 if e_pos > len(self.edit_text): e_pos = len(self.edit_text) self.edit_pos = e_pos self.pref_col_maxcol = x, maxcol self._invalidate() return True def mouse_event(self, (maxcol,), event, button, x, y, focus): """ Move the cursor to the location clicked for button 1. """ if button==1: return self.move_cursor_to_coords( (maxcol,), x, y ) def _delete_highlighted(self): """ Delete all highlighted text and update cursor position, if any text is highlighted. """ if not self.highlight: return start, stop = self.highlight btext, etext = self.edit_text[:start], self.edit_text[stop:] self.set_edit_text( btext + etext ) self.edit_pos = start self.highlight = None def render(self,(maxcol,), focus=False): """ Render edit widget and return canvas. Include cursor when in focus. """ self._shift_view_to_cursor = not not focus # force bool canv = Text.render(self,(maxcol,)) if focus: canv = CompositeCanvas(canv) canv.cursor = self.get_cursor_coords((maxcol,)) # .. will need to FIXME if I want highlight to work again #if self.highlight: # hstart, hstop = self.highlight_coords() # d.coords['highlight'] = [ hstart, hstop ] return canv def get_line_translation(self, maxcol, ta=None ): trans = Text.get_line_translation(self, maxcol, ta) if not self._shift_view_to_cursor: return trans text, ignore = self.get_text() x,y = calc_coords( text, trans, self.edit_pos + len(self.caption) ) if x < 0: return ( trans[:y] + [shift_line(trans[y],-x)] + trans[y+1:] ) elif x >= maxcol: return ( trans[:y] + [shift_line(trans[y],-(x-maxcol+1))] + trans[y+1:] ) return trans def get_cursor_coords(self,(maxcol,)): """Return the (x,y) coordinates of cursor within widget.""" self._shift_view_to_cursor = True return self.position_coords(maxcol,self.edit_pos) def position_coords(self,maxcol,pos): """ Return (x,y) coordinates for an offset into self.edit_text. """ p = pos + len(self.caption) trans = self.get_line_translation(maxcol) x,y = calc_coords(self.get_text()[0], trans,p) return x,y class IntEdit(Edit): """Edit widget for integer values""" def valid_char(self, ch): """Return true for decimal digits.""" return len(ch)==1 and ord(ch)>=ord('0') and ord(ch)<=ord('9') def __init__(self,caption="",default=None): """ caption -- caption markup default -- default edit value """ if default is not None: val = str(default) else: val = "" self.__super.__init__(caption,val) def keypress(self,(maxcol,),key): """Handle editing keystrokes. Return others.""" if key in list("0123456789"): # trim leading zeros while self.edit_pos > 0 and self.edit_text[:1] == "0": self.set_edit_pos( self.edit_pos - 1) self.set_edit_text(self.edit_text[1:]) unhandled = Edit.keypress(self,(maxcol,),key) return unhandled def value(self): """Return the numeric value of self.edit_text.""" if self.edit_text: return long(self.edit_text) else: return 0class WidgetWrap(Widget): no_cache = ["rows"] def __init__(self, w): """ w -- widget to wrap, stored as self.w This object will pass the functions defined in Widget interface definition to self.w. """ self._w = w def get_w(self): return self._w def set_w(self, w): self._w = w self._invalidate() w = property(get_w, set_w) def render(self, size, focus=False): """Render self.w.""" canv = self.w.render(size, focus=focus) return CompositeCanvas(canv) def selectable(self): return self.w.selectable() def __getattr__(self,name): """Call self.w if name is in Widget interface definition.""" if name in ['get_cursor_coords','get_pref_col','keypress', 'move_cursor_to_coords','rows','mouse_event',]: return getattr(self._w, name) raise AttributeError, nameclass SelectableIcon(Text): def selectable(self): return True def render(self, (maxcol,), focus=False): c = Text.render(self, (maxcol,), focus ) if focus: c = CompositeCanvas(c) c.cursor = self.get_cursor_coords((maxcol,)) return c def get_cursor_coords(self, (maxcol,)): if maxcol>1: return (1,0) def keypress(self, (maxcol,), key): return keyclass CheckBox(WidgetWrap): states = { True: SelectableIcon("[X]"), False: SelectableIcon("[ ]"), 'mixed': SelectableIcon("[#]") } reserve_columns = 4 def selectable(self): return True def __init__(self, label, state=False, has_mixed=False, on_state_change=None, user_data=None): """ label -- markup for check box label state -- False, True or "mixed" has_mixed -- True if "mixed" is a state to cycle through on_state_change -- callback function for state changes on_state_change( check box, new state, user_data=None) user_data -- additional param for on_press callback, ommited if None for compatibility reasons """ self.__super.__init__(None) # self.w set by set_state below self.label = Text("") self.has_mixed = has_mixed self.state = None self.on_state_change = on_state_change self.user_data = user_data self.set_label(label) self.set_state(state) def set_label(self, label): """Change the check box label.""" self.label.set_text(label) self._invalidate() def get_label(self): """Return label text.""" text, attr = self.label.get_text() return text def set_state(self, state, do_callback=True): """ Call on_state_change if do_callback is True, then change the check box state. """ if (do_callback and self.state is not None and self.on_state_change): if self.user_data is None: self.on_state_change(self, state) else: self.on_state_change(self, state, self.user_data) self.state = state self.w = Columns( [ ('fixed', self.reserve_columns, self.states[state] ), self.label ] ) self.w.focus_col = 0 self._invalidate() def get_state(self): """Return the state of the checkbox.""" return self.state def keypress(self, (maxcol,), key): """Toggle state on space or enter.""" if key not in (' ','enter'): return key self.toggle_state() def toggle_state(self): """Cycle to the next valid state.""" if self.state == False: self.set_state(True) elif self.state == True: if self.has_mixed: self.set_state('mixed') else: self.set_state(False) elif self.state == 'mixed': self.set_state(False) self._invalidate() def mouse_event(self, (maxcol,), event, button, x, y, focus): """Toggle state on button 1 press.""" if button != 1 or not is_mouse_press(event): return False self.toggle_state() return True class RadioButton(WidgetWrap): states = { True: SelectableIcon("(X)"), False: SelectableIcon("( )"), 'mixed': SelectableIcon("(#)") } reserve_columns = 4 def selectable(self): return True def __init__(self, group, label, state="first True", on_state_change=None, user_data=None): """ group -- list for radio buttons in same group label -- markup for radio button label state -- False, True, "mixed" or "first True" on_state_change -- callback function for state changes on_state_change( radio_button, new_state, user_data=None) user_data -- additional param for on_press callback, ommited if None for compatibility reasons This function will append the new radio button to group. "first True" will set to True if group is empty. """ self.__super.__init__(None) # self.w set by set_state below if state=="first True": state = not group self.group = group self.label = Text("") self.state = None self.on_state_change = on_state_change self.user_data = user_data self.set_label(label) self.set_state(state) group.append(self) def set_label(self, label): """Change the check box label.""" self.label.set_text(label) self._invalidate() def get_label(self): """Return label text.""" text, attr = self.label.get_text() return text def set_state(self, state, do_callback=True): """ Call on_state_change if do_callback is True, then change the radio button state. if state is True set all other radio buttons in group to False. """ if (do_callback and self.state is not None and self.on_state_change): if self.user_data is None: self.on_state_change(self, state) else: self.on_state_change(self, state, self.user_data) self.state = state self.w = Columns( [ ('fixed', self.reserve_columns, self.states[state] ), self.label ] ) self.w.focus_col = 0 self._invalidate() if state is not True: return for cb in self.group: if cb is self: continue if cb.state: cb.set_state(False) def get_state(self): """Return the state of the radio button.""" return self.state def keypress(self, (maxcol,), key): """Set state to True on space or enter.""" if key not in (' ','enter'): return key
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -