# WORK IN PROGRESS # # $Id: /work/modules/tkinter3000/WCK/coreInput.py 179 2004-04-10T13:23:50.125998Z fredrik $ # WCK core input implementation # # Copyright (c) 2004 by Fredrik Lundh # # FIXME: refactor into mixin and simple implementation # FIXME: finish selection support # FIXME: horizontal scrolling # FIXME: mouse support: click to set cursor # FIXME: mouse support: drag to set selection # FIXME: clipboard handling # FIXME: virtual events (?) # FIXME: add support for flashing insertion cursor # FIXME: more bindings import WCK ## # Standard controller for text input widgets. The widget should # implement ui_input_get and ui_input_set methods. class InputController(WCK.Controller): def create(self, handle): handle("", self.handle_key) handle("", self.handle_backspace) handle("", self.handle_delete) handle("", self.handle_left) handle("", self.handle_right) handle("", self.handle_home) handle("", self.handle_end) handle("", self.handle_button_1) def handle_key(self, event): widget = event.widget if event.char and event.char >= " ": text, cursor, select = widget.ui_input_get() if select: text = text[:select[0]] + text[select[1]:] text = text[:cursor] + event.char + text[cursor:] cursor = cursor + len(event.char) select = None widget.ui_input_set(text, cursor, select) elif event.char == "\x01": # control-a: select all text, cursor, select = widget.ui_input_get() widget.ui_input_set(text, cursor, (0, len(text))) def handle_backspace(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() if cursor > 0: text = text[:cursor-1] + text[cursor:] cursor = cursor - 1 widget.ui_input_set(text, cursor, None) def handle_delete(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() text = text[:cursor] + text[cursor+1:] widget.ui_input_set(text, cursor, None) def handle_left(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() widget.ui_input_set(text, cursor-1, None) def handle_right(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() widget.ui_input_set(text, cursor+1, None) def handle_home(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() widget.ui_input_set(text, 0, None) def handle_end(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() widget.ui_input_set(text, len(text), None) def handle_button_1(self, event): widget = event.widget widget.focus_set() ## # Emacs-style controller for text input widgets. This adds a number # of emacs control codes to the standard controller. class EmacsInputController(InputController): def handle_key(self, event): char = event.char if char and char < " ": control = chr(ord(char) + 64) if control == "A": self.handle_home(event) elif control == "E": self.handle_end(event) elif control == "F": self.handle_right(event) elif control == "B": self.handle_left(event) elif control == "D": self.handle_delete(event) elif control == "K": self.handle_kill(event) else: return InputController.handle_key(self, event) def handle_kill(self, event): widget = event.widget text, cursor, select = widget.ui_input_get() widget.ui_input_set(text[:cursor], cursor, None) ## # Core input widget. # # @param master The parent widget. # @param **options Widget configuration options. The input widget # supports all standard options, plus the options listed below. # @keyparam font Input font. # @keyparam foreground Field background (defaults to white). # @keyparam background Field foreground (defaults to black). # @keyparam height Field height, in character units. # @keyparam width Field width, in character units. class Input(WCK.Widget): ui_option_font = WCK.FONT ui_option_background = WCK.BACKGROUND ui_option_foreground = WCK.FOREGROUND ui_option_height = 1.25 ui_option_width = 25 ui_option_relief = "sunken" ui_option_borderwidth = 2 ui_option_takefocus = 1 ui_controller = InputController def __init__(self, master, **options): self.__text = "" self.__cursor = 0 self.__focus = None self.__select = None self.ui_init(master, options) def ui_handle_config(self): self.__font = self.ui_font( self.ui_option_foreground, self.ui_option_font ) option_width = float(self.ui_option_width) option_height = float(self.ui_option_height) width, height = self.__font.measure() return int(option_width * width) + 2, int(option_height * height) + 2 def ui_handle_clear(self, draw, x0, y0, x1, y1): draw.rectangle((x0, y0, x1, y1), self.ui_brush("white")) def ui_handle_repair(self, draw, x0, y0, x1, y1): if self.__focus: if self.__select: b, e = self.__select x0, h = self.__font.measure(self.__text[:b]) x1, h = self.__font.measure(self.__text[:e]) draw.rectangle( (x0+2, 0, x1+2, y1), self.ui_brush("gold") ) x, h = self.__font.measure(self.__text[:self.__cursor]) x = x + 2; y = 2 draw.rectangle( (x, y-1, x+1, y+h+1), self.ui_brush(self.ui_option_foreground) ) draw.text((2, 2), self.__text, self.__font) def ui_handle_focus(self, draw, focus): self.__focus = focus if focus: self.__cursor = 0 self.__select = 0, len(self.__text) self.ui_damage() ## # (Controller hook) Returns current editor contents. def ui_input_get(self): return self.__text, self.__cursor, self.__select ## # (Controller hook) Updates the editor contents. def ui_input_set(self, text, cursor, select): if self.__text != text: # FIXME: call validation hook pass self.__text = text if cursor < 0: cursor = 0 elif cursor > len(text): cursor = len(text) self.__cursor = cursor self.__select = select self.ui_damage() ## # Returns current string. def get(self): return self.__text ## # Replaces the current string. # # @param text String. # @param select If true, select def set(self, text, select=0): if select: select = 0, len(text) else: select = None self.ui_input_set(text, 0, select)