diff options
-rw-r--r-- | src/caribou.py | 34 | ||||
-rw-r--r-- | src/keyboard.py | 167 |
2 files changed, 152 insertions, 49 deletions
diff --git a/src/caribou.py b/src/caribou.py index 8d51c83..6c8c93b 100644 --- a/src/caribou.py +++ b/src/caribou.py @@ -41,18 +41,28 @@ class Test: def __set_text_location(self, acc): text = acc.queryText() [x, y, width, height] = text.getCharacterExtents(text.caretOffset, pyatspi.DESKTOP_COORDS) - cp.set_cursor_location(x, y + height) + cp.set_cursor_location(gtk.gdk.Rectangle(x, y, width, height)) + + component = acc.queryComponent() + entry_bb = component.getExtents(pyatspi.DESKTOP_COORDS) + cp.set_entry_location(entry_bb) cp.show_all() def __set_entry_location(self, acc): text = acc.queryText() - [x, y, width, height] = text.getCharacterExtents(text.caretOffset, pyatspi.DESKTOP_COORDS) - if x == 0 and y == 0 and width == 0 and height == 0: - component = acc.queryComponent() - bb = component.getExtents(pyatspi.DESKTOP_COORDS) - cp.set_cursor_location(bb.x, bb.y + bb.height) - else: - cp.set_cursor_location(x, y + height) + cursor_bb = gdk.Rectangle( + *text.getCharacterExtents(text.caretOffset, + pyatspi.DESKTOP_COORDS)) + + component = acc.queryComponent() + entry_bb = component.getExtents(pyatspi.DESKTOP_COORDS) + + if cursor_bb == gdk.Rectangle(0, 0, 0, 0): + cursor_bb = entry_bb + + cp.set_cursor_location(cursor_bb) + cp.set_entry_location(entry_bb) + cp.show_all() def on_state_changed_focused(self, event): @@ -165,7 +175,13 @@ if __name__ == "__main__": pyatspi.Registry.registerKeystrokeListener(test.on_key_down, mask = None, kind = (pyatspi.KEY_PRESSED_EVENT,)) # TODO: move text entry detection to its own file - cp = keyboard.CaribouWindow() + placement = keyboard.CaribouKeyboardPlacement( + xalign=keyboard.CaribouKeyboardPlacement.START, + xstickto=keyboard.CaribouKeyboardPlacement.ENTRY, + ystickto=keyboard.CaribouKeyboardPlacement.ENTRY, + xgravitate=keyboard.CaribouKeyboardPlacement.INSIDE, + ygravitate=keyboard.CaribouKeyboardPlacement.INSIDE) + cp = keyboard.CaribouHoverWindow(placement) cp.hide_all() gtk.main() diff --git a/src/keyboard.py b/src/keyboard.py index 47c8ccd..b4943f3 100644 --- a/src/keyboard.py +++ b/src/keyboard.py @@ -127,62 +127,149 @@ class CaribouKeyboard(gtk.Frame): gobject.type_register(CaribouKeyboard) -class CaribouWindow(gtk.VBox): +class CaribouWindow(gtk.Window): __gtype_name__ = "CaribouWindow" def __init__(self): - super(CaribouWindow, self).__init__() + super(CaribouWindow, self).__init__(gtk.WINDOW_POPUP) self.set_name("CaribouWindow") - self.__toplevel = gtk.Window(gtk.WINDOW_POPUP) - self.__toplevel.add(self) - self.__toplevel.connect("size-allocate", lambda w, a: self.__check_position()) - self.__cursor_location = (0, 0) - self.pack_start(CaribouKeyboard(qwerty)) + self._vbox = gtk.VBox() + self.add(self._vbox) - def set_cursor_location(self, x, y): - #print "----> SET CURSOR LOCATION" - self.__cursor_location = (x, y) - self.__check_position() + self._vbox.pack_start(CaribouKeyboard(qwerty)) - def do_size_request(self, requisition): - #print "---->> DO SIZE REQUEST" - gtk.VBox.do_size_request(self, requisition) - self.__toplevel.resize(1, 1) +class CaribouHoverWindow(CaribouWindow): + __gtype_name__ = "CaribouHoverWindow" - def __check_position(self): - #print "---->>> CHECK POSITION" - bx = self.__cursor_location[0] + self.__toplevel.allocation.width - by = self.__cursor_location[1] + self.__toplevel.allocation.height + def __init__(self, default_placement=None): + super(CaribouHoverWindow, self).__init__() - root_window = gdk.get_default_root_window() - sx, sy = root_window.get_size() - - if bx > sx: - x = sx - self.__toplevel.allocation.width - else: - x = self.__cursor_location[0] + self.connect("size-allocate", lambda w, a: self._update_position()) - if by > sy: - y = sy - self.__toplevel.allocation.height - else: - y = self.__cursor_location[1] + self._cursor_location = gtk.gdk.Rectangle() + self._entry_location = gtk.gdk.Rectangle() + self._default_placement = default_placement or \ + CaribouKeyboardPlacement() + - self.move(x, y) + def set_cursor_location(self, cursor_location): + self._cursor_location = cursor_location + self._update_position() - def show_all(self): - gtk.VBox.show_all(self) - self.__toplevel.show_all() + def set_entry_location(self, entry_location): + self._entry_location = entry_location + self._update_position() - def hide_all(self): - gtk.VBox.hide_all(self) - self.__toplevel.hide_all() + def set_default_placement(self, default_placement): + self._default_placement = default_placement + self._update_position() - def move(self, x, y): - self.__toplevel.move(x, y) + def _get_root_bbox(self): + root_window = gdk.get_default_root_window() + args = root_window.get_position() + root_window.get_size() + return gdk.Rectangle(*args) + + def _calculate_position(self, placement=None): + root_bbox = self._get_root_bbox() + placement = placement or self._default_placement + + x = self._calculate_axis(placement.x, root_bbox) + y = self._calculate_axis(placement.y, root_bbox) + + proposed_position = \ + gdk.Rectangle(x, y, self.allocation.width, self.allocation.height) + + x += placement.x.adjust_to_bounds(root_bbox, proposed_position) + y += placement.y.adjust_to_bounds(root_bbox, proposed_position) + + return x, y + + def _update_position(self): + self.move(*self._calculate_position()) + + def _calculate_axis(self, axis_placement, root_bbox): + bbox = root_bbox + + if axis_placement.stickto == CaribouKeyboardPlacement.CURSOR: + bbox = self._cursor_location + elif axis_placement.stickto == CaribouKeyboardPlacement.ENTRY: + bbox = self._entry_location + + offset = axis_placement.get_offset(bbox) + + if axis_placement.align == CaribouKeyboardPlacement.END: + offset += axis_placement.get_length(bbox) + if axis_placement.gravitate == CaribouKeyboardPlacement.INSIDE: + offset -= axis_placement.get_length(self.allocation) + elif axis_placement.align == CaribouKeyboardPlacement.START: + if axis_placement.gravitate == CaribouKeyboardPlacement.OUTSIDE: + offset -= axis_placement.get_length(self.allocation) + elif axis_placement.align == CaribouKeyboardPlacement.CENTER: + offset += axis_placement.get_length(bbox)/2 + + return offset + +class CaribouKeyboardPlacement(object): + START = 'start' + END = 'end' + CENTER = 'center' + + SCREEN = 'screen' + ENTRY = 'entry' + CURSOR = 'cursor' + + INSIDE = 'inside' + OUTSIDE = 'outside' + + class _AxisPlacement(object): + def __init__(self, axis, align, stickto, gravitate): + self.axis = axis + self.align = align + self.stickto = stickto + self.gravitate = gravitate + + def copy(self, align=None, stickto=None, gravitate=None): + return self.__class__(self.axis, + align or self.align, + stickto or self.stickto, + gravitate or self.gravitate) + + def get_offset(self, bbox): + return bbox.x if self.axis == 'x' else bbox.y + + def get_length(self, bbox): + return bbox.width if self.axis == 'x' else bbox.height + + def adjust_to_bounds(self, root_bbox, child_bbox): + child_vector_start = self.get_offset(child_bbox) + child_vector_end = self.get_length(child_bbox) + child_vector_start + root_vector_start = self.get_offset(root_bbox) + root_vector_end = self.get_length(root_bbox) + root_vector_start + + if root_vector_end < child_vector_end: + return root_vector_end - child_vector_end + + if root_vector_start > child_vector_start: + return root_vector_start - child_vector_start + + return 0 + + + def __init__(self, + xalign=None, xstickto=None, xgravitate=None, + yalign=None, ystickto=None, ygravitate=None): + self.x = self._AxisPlacement('x', + xalign or self.END, + xstickto or self.CURSOR, + xgravitate or self.OUTSIDE) + self.y = self._AxisPlacement('y', + yalign or self.END, + ystickto or self.CURSOR, + ygravitate or self.OUTSIDE) if __name__ == "__main__": - ckbd = CaribouWindow() + ckbd = CaribouHoverWindow() ckbd.show_all() gtk.main() |