# 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)