From d108520f37360c5edc0ac20a1511cae352aa786c Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Thu, 20 Aug 2009 13:02:56 +0000 Subject: Implement smooth scrolling in TableView --- (limited to 'src') diff --git a/src/jarabe/journal/browse/tableview.py b/src/jarabe/journal/browse/tableview.py index 34fe42b..43a871b 100644 --- a/src/jarabe/journal/browse/tableview.py +++ b/src/jarabe/journal/browse/tableview.py @@ -36,26 +36,30 @@ class TableCell: def on_release(self, widget, event): pass -class TableView(gtk.Viewport): - def __init__(self, cell_class, rows, cols): - gobject.GObject.__init__(self) - - self._cell_class = cell_class +class _SmoothTable(gtk.Bin): + __gsignals__ = { + 'set_scroll_adjustments': (gobject.SIGNAL_RUN_LAST, None, + (gtk.Adjustment, gtk.Adjustment)), + } + + def __init__(self, cells, cell_class, rows, cols): + self._smooth_adjustment = None + self._smooth_window = None self._rows = rows - self._cols = cols - self._cells = [] - self._model = None - self._hover_selection = True - self._full_adjustment = None - self._full_height = 0 - self._selected_cell = None + self._heigth = 0 + self.selected_cell = None + self.hover_selection = False - self._table = gtk.Table() - self._table.show() + gtk.Bin.__init__(self) - for y in range(self._rows + 1): - self._cells.append(self._cols * [None]) - for x in range(self._cols): + table = gtk.Table() + table.show() + self.add(table) + + for y in range(rows + 1): + cells.append(cols * [None]) + + for x in range(cols): canvas = hippo.Canvas() canvas.show() canvas.modify_bg(gtk.STATE_NORMAL, @@ -66,40 +70,161 @@ class TableView(gtk.Viewport): canvas.set_root(sel_box) canvas.root = sel_box - cell = self._cell_class() + cell = cell_class() sel_box.append(cell, hippo.PACK_EXPAND) - if self._hover_selection: - canvas.connect('enter-notify-event', - self.__enter_notify_event_cb, cell) - canvas.connect('leave-notify-event', - self.__leave_notify_event_cb) + canvas.connect('enter-notify-event', + self.__enter_notify_event_cb, cell) + canvas.connect('leave-notify-event', + self.__leave_notify_event_cb) canvas.connect('button-release-event', self.__button_release_event_cb, cell) - self._table.attach(canvas, x, x + 1, y, y + 1, + table.attach(canvas, x, x + 1, y, y + 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0) - self._cells[y][x] = (canvas, cell) + cells[y][x] = (canvas, cell) + + def __button_release_event_cb(self, widget, event, cell): + cell.on_release(widget, event) + + def __enter_notify_event_cb(self, canvas, event, cell): + if not self.hover_selection: + return + if cell.get_visible(): + canvas.root.props.background_color = COLOR_SELECTED.get_int() + self.selected_cell = cell + + def __leave_notify_event_cb(self, canvas, event): + if not self.hover_selection: + return + canvas.root.props.background_color = COLOR_BACKGROUND.get_int() + self.selected_cell = None + + def do_size_allocate(self, allocation): + self.allocation = allocation + self._heigth = int(math.ceil(float(allocation.height) / self._rows)) + + if self.flags() & gtk.REALIZED: + self.window.move_resize(*allocation) + self._smooth_window.resize(allocation.width, + allocation.height + self._heigth) + + self._set_adjustment_upper() + self._smooth_adjustment.changed() + + table_allocation = allocation.copy() + table_allocation.height += self._heigth + self.child.size_allocate(table_allocation) + + def do_size_request(self, requisition): + requisition.width, requisition.height = self.child.size_request() + + def do_realize(self): + gtk.Bin.do_realize(self) + + self.window = gtk.gdk.Window(self.get_parent_window(), + window_type=gtk.gdk.WINDOW_CHILD, + x=self.allocation.x, + y=self.allocation.y, + width=self.allocation.width, + height=self.allocation.height, + wclass=gtk.gdk.INPUT_OUTPUT, + colormap=self.get_colormap(), + event_mask=gtk.gdk.VISIBILITY_NOTIFY_MASK) + + self.window.set_user_data(self) + self.set_style(self.style.attach(self.window)) + self.style.set_background(self.window, gtk.STATE_NORMAL) + + self._smooth_window = gtk.gdk.Window(self.window, + window_type=gtk.gdk.WINDOW_CHILD, + x=0, + y=int(-self._smooth_adjustment.value), + width=self.allocation.width, + height=self.allocation.height + self._heigth, + colormap=self.get_colormap(), + wclass=gtk.gdk.INPUT_OUTPUT, + event_mask=(self.get_events() | gtk.gdk.EXPOSURE_MASK | \ + gtk.gdk.SCROLL_MASK)) + + self._smooth_window.set_user_data(self) + self.style.set_background(self._smooth_window, gtk.STATE_NORMAL) + self.child.set_parent_window(self._smooth_window) + + self.queue_resize() + + def do_unrealize(self): + self._smooth_window.set_user_data(None) + self._smooth_window.destroy() + self._smooth_window = None + gtk.Bin.do_unrealize(self) + + def do_map(self): + gtk.Bin.do_map(self) + self._smooth_window.show() + self.window.show() + + def do_set_scroll_adjustments(self, hadjustment, vadjustment): + if vadjustment is None or vadjustment == self._smooth_adjustment: + return + + if self._smooth_adjustment is not None: + self._smooth_adjustment.disconnect_by_func(self.__value_changed_cb) + + self._smooth_adjustment = vadjustment + self._set_adjustment_upper() + vadjustment.connect('value-changed', self.__value_changed_cb) + self.__value_changed_cb() + + def __value_changed_cb(self, sender=None): + if not self.flags() & gtk.REALIZED: + return + self._smooth_window.move(0, int(-self._smooth_adjustment.value)) + self.window.process_updates(True) + + def _set_adjustment_upper(self): + adj = self._smooth_adjustment + adj.page_size = 0 + adj.page_increment = 0 + adj.lower = 0 + + if self._heigth != adj.upper: + adj.upper = self._heigth + adj.changed() + + if adj.value > adj.upper: + adj.value = adj.upper + adj.value_changed() + +class TableView(gtk.Bin): + __gsignals__ = { + 'set_scroll_adjustments': (gobject.SIGNAL_RUN_LAST, None, + (gtk.Adjustment, gtk.Adjustment)), + } + + def __init__(self, cell_class, rows, cols): + self._rows = rows + self._cols = cols + self._cells = [] + self._model = None + self._full_adjustment = None + self._cell_height = 0 + self._top_cell = None + self._last_adjustment = -1 + + gtk.Bin.__init__(self) + + self._table = _SmoothTable(self._cells, cell_class, rows, cols) + self._table.show() smooth_box = gtk.ScrolledWindow() smooth_box.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER) smooth_box.show() - smooth_box.add_with_viewport(self._table) + smooth_box.add(self._table) self.add(smooth_box) self.connect('key-press-event', self.__key_press_event_cb) - def do_set_scroll_adjustments(self, hadj, vadj): - if vadj is None: - return - if self._full_adjustment is not None: - self._full_adjustment.disconnect_by_func( - self.__adjustment_value_changed) - self._full_adjustment = vadj - self._full_adjustment.connect('value-changed', - self.__adjustment_value_changed) - self._setup_adjustment() - def get_size(self): return (self._cols, self._rows) @@ -108,9 +233,10 @@ class TableView(gtk.Viewport): return (frame[0],) def set_cursor(self, cursor): - if self._full_adjustment is None: + if self._full_adjustment is None or self._cell_height == 0: return - #self._full_adjustment.props.value = cursor + self._full_adjustment.props.value = self._cell_height * cursor + self._full_adjustment.changed() def get_model(self): return self._model @@ -132,10 +258,10 @@ class TableView(gtk.Viewport): getter=get_model, setter=set_model) def get_hover_selection(self): - return self._hover_selection + return self._table.hover_selection def set_hover_selection(self, value): - self._hover_selection = value + self._table.hover_selection = value hover_selection = gobject.property(type=object, getter=get_hover_selection, setter=set_hover_selection) @@ -144,52 +270,105 @@ class TableView(gtk.Viewport): frame = self._get_frame() return ((frame[0],), (frame[1],)) - def do_size_allocate(self, alloc): - gtk.Viewport.do_size_allocate(self, alloc) - self._full_height = alloc.height + 100 - self._table.set_size_request(-1, self._full_height) + def do_set_scroll_adjustments(self, hadjustment, vadjustment): + if vadjustment is None or vadjustment == self._full_adjustment: + return + if self._full_adjustment is not None: + self._full_adjustment.disconnect_by_func( + self.__adjustment_value_changed) + self._full_adjustment = vadjustment + self._full_adjustment.connect('value-changed', + self.__adjustment_value_changed) self._setup_adjustment() + def do_size_allocate(self, allocation): + self.child.size_allocate(allocation) + self._cell_height = allocation.height / self._rows + self._setup_adjustment() + + def do_size_request(self, requisition): + requisition.width, requisition.height = self.child.size_request() + def _fillin_cell(self, canvas, cell): + if self._table.selected_cell == cell and cell.get_visible(): + bg_color = COLOR_SELECTED + else: + bg_color = COLOR_BACKGROUND + canvas.root.props.background_color = bg_color.get_int() + if cell.row is None: cell.set_visible(False) else: cell.fillin() cell.set_visible(True) - bg_color = COLOR_BACKGROUND - if self._selected_cell == cell: - if cell.get_visible(): - bg_color = COLOR_SELECTED canvas.root.props.background_color = bg_color.get_int() def _setup_adjustment(self): - if self._full_adjustment is None or self._full_height == 0: + if self._full_adjustment is None or self._cell_height == 0: return - adj = self._full_adjustment.props + adj = self._full_adjustment if self._model is None: adj.upper = 0 adj.value = 0 return - if self._cols == 0: - adj.upper = 0 - else: - count = self._model.iter_n_children(None) - adj.upper = int(math.ceil(float(count) / self._cols)) + count = self._model.iter_n_children(None) - adj.value = min(adj.value, adj.upper - self._rows) - adj.page_size = self._rows - adj.page_increment = self._rows + adj.upper = max(0, math.ceil(float(count) / self._cols) * \ + self._cell_height) + adj.value = min(adj.value, adj.upper) + adj.page_size = self._cell_height * self._rows + adj.page_increment = adj.page_size self._full_adjustment.changed() - self.__adjustment_value_changed(self._full_adjustment) + self.__adjustment_value_changed(self._full_adjustment, force=True) def _get_frame(self): - return (int(self._full_adjustment.props.value) * self._cols, - (int(self._full_adjustment.props.value) + self._rows) * self._cols - 1) + adj = self._full_adjustment + return (int(adj.value / self._cell_height) * self._cols, + (int(adj.value / self._cell_height) + self._rows) * \ + self._cols - 1) + + def __adjustment_value_changed(self, adjustment, force=False): + if self._last_adjustment == int(adjustment.value): + return + self._last_adjustment = int(adjustment.value) + + cell_row = int(adjustment.props.value) / self._cell_height + cell_num = cell_row * self._cols + + smooth_adj = self.child.props.vadjustment + smooth_adj_value = int(min(smooth_adj.upper, + int(adjustment.props.value) - (cell_row * self._cell_height))) + + if cell_num == self._top_cell and not force: + smooth_adj.value = smooth_adj_value + smooth_adj.value_changed() + return + self._top_cell = cell_num + + if self._model is not None: + count = self._model.iter_n_children(None) + else: + count = 0 + + for y in range(self._rows + 1): + for x in range(self._cols): + canvas, cell = self._cells[y][x] + + cell.row = None + if cell_num < count: + cell.row = self._model.get_row((cell_num,), + self._get_frame()) + + self._fillin_cell(canvas, cell) + cell_num += 1 + + smooth_adj.value = smooth_adj_value + smooth_adj.value_changed() def __row_changed_cb(self, model, path, iter): range = self._get_frame() @@ -207,58 +386,35 @@ class TableView(gtk.Viewport): self._setup_adjustment() def __key_press_event_cb(self, widget, event): - if self._full_adjustment is None or self._full_height == 0: + if self._full_adjustment is None or self._cell_height == 0: return adj = self._full_adjustment.props - uplimit = adj.upper - self._rows + page = self._rows * self._cell_height + uplimit = adj.upper - page if event.keyval == gtk.keysyms.Up: - adj.value -= 1 + adj.value -= self._cell_height + elif event.keyval == gtk.keysyms.Down: - if adj.value + 1 <= uplimit: - adj.value += 1 + adj.value += min(uplimit - adj.value, self._cell_height) + elif event.keyval in (gtk.keysyms.Page_Up, gtk.keysyms.KP_Page_Up): - adj.value -= self._rows + adj.value -= min(adj.value, page) + elif event.keyval in (gtk.keysyms.Page_Down, gtk.keysyms.KP_Page_Down): - if adj.value + self._rows <= uplimit: - adj.value += self._rows + adj.value += min(uplimit - adj.value, page) + elif event.keyval in (gtk.keysyms.Home, gtk.keysyms.KP_Home): adj.value = 0 + elif event.keyval in (gtk.keysyms.End, gtk.keysyms.KP_End): adj.value = uplimit + else: return False return True - def __button_release_event_cb(self, widget, event, cell): - cell.on_release(widget, event) - - def __enter_notify_event_cb(self, canvas, event, cell): - if cell.get_visible(): - canvas.root.props.background_color = COLOR_SELECTED.get_int() - self._selected_cell = cell - - def __leave_notify_event_cb(self, canvas, event): - canvas.root.props.background_color = COLOR_BACKGROUND.get_int() - self._selected_cell = None - - def __adjustment_value_changed(self, adjustment): - if self._model: - count = self._model.iter_n_children(None) - else: - count = 0 - cell_num = int(adjustment.props.value) * self._cols - - for y in range(self._rows): - for x in range(self._cols): - canvas, cell = self._cells[y][x] - - cell.row = None - if cell_num < count: - cell.row = self._model.get_row((cell_num,), - self._get_frame()) - - self._fillin_cell(canvas, cell) - cell_num += 1 +TableView.set_set_scroll_adjustments_signal('set-scroll-adjustments') +_SmoothTable.set_set_scroll_adjustments_signal('set-scroll-adjustments') diff --git a/src/jarabe/journal/thumbsview.py b/src/jarabe/journal/thumbsview.py index 4dca8df..920bf2c 100644 --- a/src/jarabe/journal/thumbsview.py +++ b/src/jarabe/journal/thumbsview.py @@ -21,14 +21,19 @@ import hippo from jarabe.journal.browse.lazymodel import Source from jarabe.journal.browse.tableview import TableView, TableCell +from jarabe.journal.browse.source import * class ThumbsCell(TableCell, hippo.CanvasBox): def __init__(self): TableCell.__init__(self) hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_VERTICAL) - label = hippo.CanvasWidget(widget=gtk.Button('!!!')) - self.append(label) + self.label = hippo.CanvasText( + size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END) + self.append(self.label) + + def fillin(self): + self.label.props.text = self.row[FIELD_TITLE] or '' class ThumbsView(TableView): __gsignals__ = { -- cgit v0.9.1