From 44bde3ac3156146be9510124600dc6f3145aa335 Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Sat, 27 Oct 2012 20:19:00 +0000 Subject: fixed positioning of textview for OSK --- diff --git a/SlideruleActivity.py b/SlideruleActivity.py index afa2b67..b281490 100644 --- a/SlideruleActivity.py +++ b/SlideruleActivity.py @@ -38,6 +38,7 @@ from sugar3.graphics.toolbarbox import ToolbarBox from sugar3.bundle.activitybundle import ActivityBundle from sugar3.activity.widgets import ActivityToolbarButton, StopButton, \ EditToolbar +from sugar3.graphics import style from sugar3.graphics.toolbarbox import ToolbarButton from sugar3.datastore import datastore @@ -78,18 +79,32 @@ class SlideruleActivity(activity.Activity): self._setup_toolbars() - canvas = Gtk.DrawingArea() - canvas.set_size_request(Gdk.Screen.width(), - Gdk.Screen.height()) - self.set_canvas(canvas) - canvas.show() + self.fixed = Gtk.Fixed() + self.fixed.connect('size-allocate', self._fixed_resize_cb) + self.fixed.show() + self.set_canvas(self.fixed) + + self.vbox = Gtk.VBox(False, 0) + self.vbox.set_size_request( + Gdk.Screen.width(), Gdk.Screen.height() - style.GRID_CELL_SIZE) + self.fixed.put(self.vbox, 0, 0) + self.vbox.show() + + self._canvas = Gtk.DrawingArea() + self._canvas.set_size_request(int(Gdk.Screen.width()), + int(Gdk.Screen.height())) + self._canvas.show() + self.show_all() + self.vbox.pack_end(self._canvas, True, True, 0) + self.vbox.show() + self.show_all() self.custom_slides = [False, False] self.sr = SlideRule( - canvas, - os.path.join(activity.get_bundle_path(), 'images/'), + self.canvas, + os.path.join(activity.get_bundle_path(), 'images'), self) # Read the slider positions from the Journal @@ -164,6 +179,11 @@ class SlideruleActivity(activity.Activity): self.metadata['calculate' + str(i)] = \ self._calculate_function[i].get_text() + def _fixed_resize_cb(self, widget=None, rect=None): + ''' If a toolbar opens or closes, we need to resize the vbox + holding out scrolling window. ''' + self.vbox.set_size_request(rect.width, rect.height) + def _hide_all(self): self._hide_top() self._hide_bottom() diff --git a/sprite_factory.py b/sprite_factory.py index ea93f2a..405388b 100644 --- a/sprite_factory.py +++ b/sprite_factory.py @@ -27,6 +27,7 @@ class Stator(): else: self.spr = Sprite(sprites, x, y, svg_str_to_pixbuf(svg_engine().svg)) + self.spr.type = name self.name = name self.calculate = calculate self.result = result @@ -68,6 +69,14 @@ class Slide(Stator): self.calculate = function self.name = name + def add_textview(self, textview, i=0): + self.tabs[i].textview = textview + self.tabs[i].textbuffer = textview.get_buffer() + + def set_fixed(self, fixed): + for tab in self.tabs: + tab.fixed = fixed + def match(self, sprite): if sprite == self.spr or sprite == self.tabs[0].spr or \ sprite == self.tabs[1].spr: @@ -77,36 +86,39 @@ class Slide(Stator): def draw(self, layer=1000): self.spr.set_layer(layer) self.spr.draw() - self.tabs[0].spr.set_layer(layer) - self.tabs[0].spr.draw() - self.tabs[1].spr.set_layer(layer) - self.tabs[1].spr.draw() + for tab in self.tabs: + tab.draw() def move(self, dx, dy): self.spr.move((dx, dy)) - self.tabs[0].spr.move((dx + self.tab_dx[0], dy + self.tab_dy[0])) - self.tabs[1].spr.move((dx + self.tab_dx[1], dy + self.tab_dy[1])) + for i, tab in enumerate(self.tabs): + tab.move(dx + self.tab_dx[i], dy + self.tab_dy[i]) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) - self.tabs[0].spr.move_relative((dx, dy)) - self.tabs[1].spr.move_relative((dx, dy)) + for i, tab in enumerate(self.tabs): + tab.move_relative(dx, dy) def hide(self): self.spr.hide() - self.tabs[0].spr.hide() - self.tabs[1].spr.hide() + for tab in self.tabs: + tab.hide() + + def label(self, label, i=0): + self.tabs[i].label(label) class Reticule(Slide): """ Create a sprite for a reticle """ def __init__(self, sprites, path, name, x, y, w, h): self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) + self.spr.type = name self.tab_dx = [0, 0] self.tab_dy = [-SHEIGHT, 2 * SHEIGHT] self.tabs = [] self.tabs.append(Tab(sprites, path, 'tab', x + self.tab_dx[0], y + self.tab_dy[0], TABWIDTH, SHEIGHT)) + self.tabs[-1].textview_yoffset = int(h / 4) self.tabs.append(Tab(sprites, path, 'tab', x + self.tab_dx[1], y + self.tab_dy[1], TABWIDTH, SHEIGHT)) self.name = name @@ -143,16 +155,54 @@ class CustomStator(Stator): class Tab(): - """ Create tabs for the slide """ + """ Create tabs for the slide; include a TextView for OSK input """ def __init__(self, sprites, path, name, x, y, w, h): self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) self.spr.label = "1.0" + self.spr.type = name self.name = name + self.width = w + self.textview = None + self.textbuffer = None + self.fixed = None + self.textview_yoffset = 0 + + def label(self, label): + if self.textbuffer is not None: + self.textbuffer.set_text(label) + + def _move_textview(self, x, y): + y += self.textview_yoffset + if self.textview is not None: + if x > 0 and x < Gdk.Screen.width() - self.width and y > 0: + self.fixed.move(self.textview, x, y) + self.textview.show() + else: + self.textview.hide() + + def move(self, x, y): + self.spr.move((x, y)) + self._move_textview(x, y) + + def move_relative(self, dx, dy): + self.spr.move_relative((dx, dy)) + x, y = self.spr.get_xy() + self._move_textview(x, y) + + def draw(self, layer=100): + self.spr.set_layer(layer) + self.spr.draw() + x, y = self.spr.get_xy() + self._move_textview(x, y) + + def hide(self): + self.spr.hide() + def file_to_pixbuf(path, name, w, h): """ Load pixbuf from a file. """ return GdkPixbuf.Pixbuf.new_from_file_at_size( - os.path.join(path+name+'.svg'), int(w), int(h)) + os.path.join(path, name + '.svg'), int(w), int(h)) def svg_str_to_pixbuf(svg_string): diff --git a/sprites.py b/sprites.py index 8cad6a6..d1a4562 100644 --- a/sprites.py +++ b/sprites.py @@ -141,7 +141,6 @@ class Sprites: else: self.cr = cr if cr is None: - print 'sprites.redraw_sprites: no Cairo context' return for spr in self.list: if area == None: @@ -328,7 +327,6 @@ class Sprite: if cr is None: cr = self._sprites.cr if cr is None: - print 'sprite.draw: no Cairo context.' return for i, img in enumerate(self.images): if isinstance(img, GdkPixbuf.Pixbuf): diff --git a/window.py b/window.py index 113cd12..e3b52f0 100644 --- a/window.py +++ b/window.py @@ -33,6 +33,7 @@ graphics associated with your slide and stator. import pygtk pygtk.require('2.0') from gi.repository import Gtk, Gdk, GdkPixbuf +from gi.repository import Pango, PangoCairo import locale from gettext import gettext as _ @@ -62,6 +63,15 @@ import logging _logger = logging.getLogger('sliderule-activity') +def _get_screen_dpi(): + xft_dpi = Gtk.Settings.get_default().get_property('gtk-xft-dpi') + dpi = float(xft_dpi / 1024) + # HACKITY HACK for XO hardware + if dpi == 200: + return 133 + return dpi + + class SlideRule(): def __init__(self, canvas, path, parent=None): @@ -82,7 +92,6 @@ class SlideRule(): 'custom2':[Custom_stator_generator]} self.path = path - self.activity = parent if parent is None: self.sugar = False @@ -106,7 +115,7 @@ class SlideRule(): self.canvas.set_can_focus(True) self.canvas.grab_focus() self.width = Gdk.Screen.width() - self.height = Gdk.Screen.height()-GRID_CELL_SIZE + self.height = Gdk.Screen.height() - GRID_CELL_SIZE self.sprites = Sprites(self.canvas) self.slides = [] self.stators = [] @@ -137,15 +146,62 @@ class SlideRule(): 150, SCREENOFFSET + SHEIGHT, 100, 2 * SHEIGHT) self.reticule.draw(2000) + self.press = None + self.last = None + self.dragpos = 0 + + # We need textviews for keyboard input from the on-screen keyboard + self._set_screen_dpi() + font_desc = Pango.font_description_from_string('12') + self.text_entries = [] + self.text_buffers = [] + + w = self.reticule.tabs[0].spr.label_safe_width() + h = int(self.reticule.tabs[0].spr.label_safe_height() / 2) + for i in range(4): # Reticule top & bottom; Slider left & right + self.text_entries.append(Gtk.TextView()) + self.text_entries[-1].set_justification(Gtk.Justification.CENTER) + self.text_entries[-1].set_pixels_above_lines(4) + self.text_entries[-1].override_background_color( + Gtk.StateType.NORMAL, Gdk.RGBA(0, 0, 0, 0)) + self.text_entries[-1].modify_font(font_desc) + self.text_buffers.append(self.text_entries[-1].get_buffer()) + self.text_entries[-1].set_size_request(w, h) + self.text_entries[-1].show() + if self.parent is not None: + self.parent.fixed.put(self.text_entries[-1], 0, 0) + self.parent.fixed.show() + self.text_entries[-1].connect('focus-out-event', + self._text_focus_out_cb) + self.reticule.add_textview(self.text_entries[0], i=BOTTOM) + self.reticule.add_textview(self.text_entries[1], i=TOP) + if self.parent is not None: + self.reticule.set_fixed(self.parent.fixed) + for slide in self.slides: + slide.add_textview(self.text_entries[2], i=LEFT) + slide.add_textview(self.text_entries[3], i=RIGHT) + if self.parent is not None: + slide.set_fixed(self.parent.fixed) + self.active_slide = self.name_to_slide('C') self.active_stator = self.name_to_stator('D') - self.update_slide_labels() self.update_result_label() - self.press = None - self.last = None - self.dragpos = 0 + def _text_focus_out_cb(self, widget=None, event=None): + ''' One of the four textviews was in focus ''' + i = None + if widget in self.text_entries: + i = self.text_entries.index(widget) + bounds = self.text_buffers[i].get_bounds() + text = self.text_buffers[i].get_text(bounds[0], bounds[1], True) + text = text.strip() + self._process_numeric_input(i, text) + + def _set_screen_dpi(self): + dpi = _get_screen_dpi() + font_map_default = PangoCairo.font_map_get_default() + font_map_default.set_resolution(dpi) def __draw_cb(self, canvas, cr): self.sprites.redraw_sprites(cr=cr) @@ -155,6 +211,8 @@ class SlideRule(): # Create the cairo context cr = self.canvas.window.cairo_create() + print 'set cr in do_expose' + self.sprites.set_cairo_context(cr) # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, @@ -172,13 +230,7 @@ class SlideRule(): k = Gdk.keyval_name(event.keyval) if self.parent is None: return - if k in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'period', - 'minus', 'Return', 'BackSpace', 'comma']: - if self.last == self.reticule.tabs[TOP].spr or \ - self.last == self.reticule.tabs[BOTTOM].spr or \ - self.last == self.active_slide.tabs[LEFT].spr: - self._process_numeric_input(self.last, k) - elif k == 'a': + if k == 'a': self.parent.show_a() elif k == 'k': self.parent.show_k() @@ -200,7 +252,7 @@ class SlideRule(): self._move_slides(self.last, 1) elif k in ['Home', 'Pause', 'Up', '^']: self._move_slides(self.name_to_stator('D').spr, - -self.name_to_stator('D').spr.get_xy()[0]) + - self.name_to_stator('D').spr.get_xy()[0]) elif k == 'r': self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() @@ -212,67 +264,19 @@ class SlideRule(): self.update_result_label() return True - def _process_numeric_input(self, sprite, keyname): - ''' Make sure numeric input is valid. ''' - CURSOR = '█' - - oldnum = sprite.labels[0].replace(CURSOR, '') - newnum = oldnum - if len(oldnum) == 0: - oldnum = '0' - if keyname == 'minus': - if oldnum == '0': - newnum = '-' - elif oldnum[0] != '-': - newnum = '-' + oldnum - else: - newnum = oldnum - elif keyname == 'comma' and self.decimal_point == ',' and \ - ',' not in oldnum: - newnum = oldnum + ',' - elif keyname == 'period' and self.decimal_point == '.' and \ - '.' not in oldnum: - newnum = oldnum + '.' - elif keyname == 'BackSpace': - if len(oldnum) > 0: - newnum = oldnum[:len(oldnum)-1] - else: - newnum = '' - elif keyname in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: - if oldnum == '0': - newnum = keyname - else: - newnum = oldnum + keyname - elif keyname == 'Return': - self.enter_value(sprite, newnum.replace(self.decimal_point, '.')) - return - else: - newnum = oldnum - if newnum == '.': - newnum = '0.' - if len(newnum) > 0 and newnum != '-': - try: - float(newnum.replace(self.decimal_point, '.')) - except ValueError, e: - newnum = oldnum - sprite.set_label(newnum + CURSOR) - - def enter_value(self, sprite, value): - if sprite is None: - return - sprite.set_label(value.replace('.', self.decimal_point)) + def _process_numeric_input(self, i, text): try: - if sprite == self.reticule.tabs[TOP].spr: - self._move_reticule_to_slide_value( - float(value.replace(self.decimal_point, '.'))) - elif sprite == self.reticule.tabs[BOTTOM].spr: - self._move_reticule_to_stator_value( - float(value.replace(self.decimal_point, '.'))) - else: - self._move_slide_to_stator_value( - float(value.replace(self.decimal_point, '.'))) - except TypeError: - sprite.set_label('NaN') + n = float(text.replace(self.decimal_point, '.')) + if i == 0: + self._move_reticule_to_stator_value(n) + elif i == 1: + self._move_reticule_to_slide_value(n) + elif i == 2: + self._move_slide_to_stator_value(n) + elif i == 3: + self._move_slide_to_stator_value(self._left_from_right(n)) + except ValueError: + self.result_label.spr.labels[0] = _('NaN') + ' ' + text return def _process_text_field(self, text_field): @@ -534,14 +538,13 @@ class SlideRule(): slidex = self.active_slide.spr.get_xy()[0] statorx = self.active_stator.spr.get_xy()[0] dx = statorx - slidex + print 'calling active slide', dx, 0 self.active_slide.move_relative(dx, 0) def _move_slides(self, sprite, dx): if self.sprite_in_stators(sprite): - for slide in self.slides: - slide.move_relative(dx, 0) - for stator in self.stators: - stator.move_relative(dx, 0) + self.active_stator.move_relative(dx, 0) + self.active_slide.move_relative(dx, 0) self.reticule.move_relative(dx, 0) elif self.reticule.match(sprite): self.reticule.move_relative(dx, 0) @@ -550,35 +553,52 @@ class SlideRule(): self.update_slide_labels() self.update_result_label() - def _update_top(self, function): - v_left = function() + def _left_from_right(self, v_right): if self.active_stator.name == 'L2': - v_right = 10 + v_left + return v_right - 10 elif self.active_stator.name == 'D': - v_right = v_left * 10. + return v_right / 10. elif self.active_stator.name == 'B': - v_right = v_left * 100. + return v_right / 100. elif self.active_stator.name == 'K2': - v_right = v_left * 1000. + return v_right / 1000. elif self.active_stator.name == 'DI': - v_right = v_left / 10. + return v_right * 10. elif self.active_stator.name == 'LLn2': - v_right = round(log(10), 2) + v_left + return v_right - round(log(10), 2) else: - v_right = v_left - for slide in self.slides: - slide.tabs[LEFT].spr.set_label(str(v_left).replace('.', - self.decimal_point)) - slide.tabs[RIGHT].spr.set_label(str(v_right).replace('.', - self.decimal_point)) - + return v_right + + def _right_from_left(self, v_left): + if self.active_stator.name == 'L2': + return 10 + v_left + elif self.active_stator.name == 'D': + return v_left * 10. + elif self.active_stator.name == 'B': + return v_left * 100. + elif self.active_stator.name == 'K2': + return v_left * 1000. + elif self.active_stator.name == 'DI': + return v_left / 10. + elif self.active_stator.name == 'LLn2': + return round(log(10), 2) + v_left + else: + return v_left + def update_slide_labels(self): """ Based on the current alignment of the rules, calculate labels. """ - self._update_top(self.active_stator.calculate) - self.reticule.tabs[BOTTOM].spr.set_label( - str(self.active_stator.result()).replace('.', self.decimal_point)) - self.reticule.tabs[TOP].spr.set_label( - str(self.active_slide.calculate()).replace('.', self.decimal_point)) + v_left = self.active_stator.calculate() + v_right = self._right_from_left(v_left) + label_left = str(v_left).replace('.', self.decimal_point) + label_right = str(v_right).replace('.', self.decimal_point) + self.active_slide.label(label_left, i=LEFT) + self.active_slide.label(label_right, i=RIGHT) + self.reticule.label( + str(self.active_stator.result()).replace('.', self.decimal_point), + i=BOTTOM) + self.reticule.label( + str(self.active_slide.calculate()).replace('.', self.decimal_point), + i=TOP) def _button_release_cb(self, win, event): if self.press == None: -- cgit v0.9.1