def update_widgets(self, new_frame=None): """ Reset the values for any Widgets in this Layout based on the current Frame data store. :param new_frame: optional old Frame - used when cloning scenes. """ for column in self._columns: for widget in column: logger.debug("Updating: %s", widget.name) # First handle the normal case - pull the default data from the current frame. if widget.name in self._frame.data: widget.value = self._frame.data[widget.name] elif widget.is_tab_stop: # Make sure every active widget is properly initialised, by calling the setter. # This will fix up any dodgy NoneType values, but preserve any values overridden # by other code. widget.value = widget.value # If an old frame was present, give the widget a chance to clone internal state # from the previous view. If there is no clone function, ignore the error. if new_frame: try: widget.clone(new_frame.find_widget(widget.name)) except AttributeError: pass
def rebase_event(self, event): """ Rebase the coordinates of the passed event to frame-relative coordinates. :param event: The event to be rebased. :returns: A new event object appropriately re-based. """ new_event = copy(event) if isinstance(new_event, MouseEvent): origin = self._canvas.origin new_event.x -= origin[0] new_event.y -= origin[1] - self._canvas.start_line logger.debug("New event: %s", new_event) return new_event
def clone(self, _, scene): """ Create a clone of this Frame into a new Screen. :param _: ignored. :param scene: The new Scene object to clone into. """ # Assume that the application creates a new set of Frames and so we need to match up the # data from the old object to the new (using the name). if self._name is not None: for effect in scene.effects: if isinstance(effect, Frame): logger.debug("Cloning: %s", effect._name) if effect._name == self._name: effect.set_theme(self._theme) effect.data = self.data for layout in self._layouts: layout.update_widgets(new_frame=effect)
def is_mouse_over(self, event, include_label=True, width_modifier=0): """ Check if the specified mouse event is over this widget. :param event: The MouseEvent to check. :param include_label: Include space reserved for the label when checking. :param width_modifier: Adjustement to width (e.g. for scroll bars). :returns: True if the mouse is over the active parts of the widget. """ # Disabled widgets should not react to the mouse. logger.debug("Widget: %s (%d, %d) (%d, %d)", self, self._x, self._y, self._w, self._h) if self._is_disabled: return False # Check for any overlap if self._y <= event.y < self._y + self._h: if ((include_label and self._x <= event.x < self._x + self._w - width_modifier) or (self._x + self._offset <= event.x < self._x + self._w - width_modifier)): return True return False
def update(self, frame_no): self._draw_label() # Calculate new visible limits if needed. height = self._h if not self._line_wrap: self._start_column = min(self._start_column, self._column) self._start_column += _find_min_start( str(self._value[self._line][self._start_column:self._column + 1]), self.width, self._frame.canvas.unicode_aware, self._column >= self.string_len(str(self._value[self._line]))) # Clear out the existing box content (colour, attr, background ) = self._pick_colours("readonly" if self._readonly else "edit_text") self._frame.canvas.clear_buffer(colour, attr, background, self._x + self._offset, self._y, self.width, height) # Convert value offset to display offsets # NOTE: _start_column is always in display coordinates. display_text = self._reflowed_text display_start_column = self._start_column display_line, display_column = 0, 0 for i, (_, line, col) in enumerate(display_text): if line < self._line or (line == self._line and col <= self._column): display_line = i display_column = self._column - col # Restrict to visible/valid content. self._start_line = max( 0, max(display_line - height + 1, min(self._start_line, display_line))) # Render visible portion of the text. for line, (text, _, _) in enumerate(display_text): if self._start_line <= line < self._start_line + height: paint_text = _enforce_width(text[display_start_column:], self.width, self._frame.canvas.unicode_aware) self._frame.canvas.paint( str(paint_text), self._x + self._offset, self._y + line - self._start_line, colour, attr, background, colour_map=paint_text.colour_map if hasattr( paint_text, "colour_map") else None) # Since we switch off the standard cursor, we need to emulate our own # if we have the input focus. if self._has_focus: line = str(display_text[display_line][0]) logger.debug("Cursor: %d,%d", display_start_column, display_column) text_width = self.string_len( line[display_start_column:display_column]) self._draw_cursor( " " if display_column >= len(line) else line[display_column], frame_no, self._x + self._offset + text_width, self._y + display_line - self._start_line)
def process_event(self, event, hover_focus): """ Process any input event. :param event: The event that was triggered. :param hover_focus: Whether to trigger focus change on mouse moves. :returns: None if the Effect processed the event, else the original event. """ # Check whether this Layout is read-only - i.e. has no active focus. if self._live_col < 0 or self._live_widget < 0: # Might just be that we've unset the focus - so check we can't find a focus. self._find_next_widget(1) if self._live_col < 0 or self._live_widget < 0: return event # Give the active widget the first refusal for this event. event = self._columns[ self._live_col][self._live_widget].process_event(event) # Check for any movement keys if the widget refused them. if event is not None: if isinstance(event, KeyboardEvent): if event.key_code == Screen.KEY_TAB: # Move on to next widget, unless it is the last in the # Layout. self._columns[self._live_col][self._live_widget].blur() self._find_next_widget(1) if self._live_col >= len(self._columns): self._live_col = 0 self._live_widget = -1 self._find_next_widget(1) return event # If we got here, we still should have the focus. self._columns[self._live_col][self._live_widget].focus() event = None elif event.key_code == Screen.KEY_BACK_TAB: # Move on to previous widget, unless it is the first in the # Layout. self._columns[self._live_col][self._live_widget].blur() self._find_next_widget(-1) if self._live_col < 0: self._live_col = len(self._columns) - 1 self._live_widget = len(self._columns[self._live_col]) self._find_next_widget(-1) return event # If we got here, we still should have the focus. self._columns[self._live_col][self._live_widget].focus() event = None elif event.key_code == Screen.KEY_DOWN: # Move on to next widget in this column wid = self._live_widget self._columns[self._live_col][self._live_widget].blur() self._find_next_widget(1, stay_in_col=True) self._columns[self._live_col][self._live_widget].focus() # Don't swallow the event if it had no effect. event = event if wid == self._live_widget else None elif event.key_code == Screen.KEY_UP: # Move on to previous widget, unless it is the first in the # Layout. wid = self._live_widget self._columns[self._live_col][self._live_widget].blur() self._find_next_widget(-1, stay_in_col=True) self._columns[self._live_col][self._live_widget].focus() # Don't swallow the event if it had no effect. event = event if wid == self._live_widget else None elif event.key_code == Screen.KEY_LEFT: # Move on to last widget in the previous column self._columns[self._live_col][self._live_widget].blur() self._find_nearest_horizontal_widget(-1) self._columns[self._live_col][self._live_widget].focus() event = None elif event.key_code == Screen.KEY_RIGHT: # Move on to first widget in the next column. self._columns[self._live_col][self._live_widget].blur() self._find_nearest_horizontal_widget(1) self._columns[self._live_col][self._live_widget].focus() event = None elif isinstance(event, MouseEvent): logger.debug("Check layout: %d, %d", event.x, event.y) if ((hover_focus and event.buttons >= 0) or event.buttons > 0): # Mouse click - look to move focus. for i, column in enumerate(self._columns): for j, widget in enumerate(column): if widget.is_mouse_over(event): self._frame.switch_focus(self, i, j) widget.process_event(event) return None return event