# -*- coding: utf-8 -*- # pylint: disable=invalid-name # ----------------------------------------------------------------------------- # Copyright (c) 2016-2017 Anaconda, Inc. # # May be copied and distributed freely only as part of an Anaconda or # Miniconda installation. # ----------------------------------------------------------------------------- """Widgets to list environemnts available to edit in the Environments tab.""" from __future__ import absolute_import, division, print_function import sys import typing from qtpy.QtCore import QSize, Qt, Signal # pylint: disable=no-name-in-module from qtpy.QtWidgets import QHBoxLayout, QPushButton # pylint: disable=no-name-in-module from anaconda_navigator.utils.qthelpers import qapplication from anaconda_navigator.utils.styles import SASS_VARIABLES from anaconda_navigator.widgets import ButtonEnvironmentOptions, FrameBase from anaconda_navigator.widgets.lists import ListWidgetBase, ListWidgetItemBase from anaconda_navigator.widgets.spinner import NavigatorSpinner # --- Widgets used in styling # ----------------------------------------------------------------------------- class WidgetEnvironment(FrameBase): """Main list item widget used in CSS styling.""" sig_entered = Signal() sig_left = Signal() clicked = Signal() def __init__(self, *args, **kwargs): """Main list item widget used in CSS styling.""" super().__init__(*args, **kwargs) self._hover = False def keyPressEvent(self, event): """Override Qt method.""" key = event.key() if key in [Qt.Key_Return, Qt.Key_Enter, Qt.Key_Space]: self.clicked.emit() super().keyPressEvent(event) def enterEvent(self, event): """Override Qt method.""" super().enterEvent(event) self._hover = True self.sig_entered.emit() def leaveEvent(self, event): """Override Qt method.""" super().enterEvent(event) self._hover = False self.sig_left.emit() def mouseReleaseEvent(self, event): """Override Qt method.""" super().enterEvent(event) if self._hover: self.clicked.emit() class ButtonEnvironmentName(QPushButton): """Button used in CSS styling.""" sig_entered = Signal() sig_left = Signal() def focusInEvent(self, event): """Override Qt method.""" super().focusInEvent(event) self.sig_entered.emit() def focusOutEvent(self, event): """Override Qt method.""" super().focusOutEvent(event) self.sig_left.emit() def enterEvent(self, event): """Override Qt method.""" super().enterEvent(event) self.sig_entered.emit() def leaveEvent(self, event): """Override Qt method.""" super().enterEvent(event) self.sig_left.emit() def setProperty(self, name, value): """Override Qt method.""" QPushButton.setProperty(self, name, value) self.style().unpolish(self) self.style().polish(self) self.update() def set_selected(self, value): """Set selected status.""" self._selected = value # pylint: disable=attribute-defined-outside-init self.setProperty('pressed', value) def set_hovered(self, value): """Set hovered status.""" self.setProperty('hovered', value) class FrameEnvironmentIcon(FrameBase): """Label used in CSS styling.""" # --- Main widgets # ----------------------------------------------------------------------------- class BaseListWidgetEnv(ListWidgetBase): """Custom list widget environments.""" def ordered_widgets(self): """Return a list of the ordered widgets.""" ordered_widgets = [] for item in self.items(): ordered_widgets.extend(item.ordered_widgets()) return ordered_widgets def setup_item(self, item): """Add additional logic after adding an item.""" index = self._items.index(item) item.widget.clicked.connect(lambda v=None, i=index: self.setCurrentRow(i, loading=True)) item.widget.clicked.connect(lambda v=None, it=item: self.sig_item_selected.emit(it)) item.widget.sig_entered.connect(lambda v=None: item.set_hovered(True)) item.button_name.sig_entered.connect(lambda v=None: self.scroll_to_item(item)) item.widget.sig_left.connect(lambda v=None: item.set_hovered(False)) item.button_name.clicked.connect(lambda v=None, it=item: self.sig_item_selected.emit(it)) item.button_name.clicked.connect(lambda v=None, i=index: self.setCurrentRow(i, loading=True)) item.button_name.sig_entered.connect(lambda v=None: item.set_hovered(True)) item.button_name.sig_left.connect(lambda v=None: item.set_hovered(False)) fm = item.button_name.fontMetrics() elided_name = fm.elidedText(item.name, Qt.ElideRight, 150) item.button_name.setText(elided_name) def text(self): """ Override Qt Method. Return full name, not elided text. """ item = self.currentItem() text = item.name if item else str() return text class BaseListItemEnv(ListWidgetItemBase): """Widget to build an item for the environments list.""" ENV_ITEM_WIDTH: typing.ClassVar[int] = SASS_VARIABLES.WIDGET_ENVIRONMENT_TOTAL_WIDTH ENV_ITEM_HEIGHT: typing.ClassVar[int] = SASS_VARIABLES.WIDGET_ENVIRONMENT_TOTAL_HEIGHT def __init__(self, name=None, prefix=None): """Widget to build an item for the environments list.""" super().__init__() self._selected = False self._name = name self._prefix = prefix # Widgets self.button_name = ButtonEnvironmentName(name) self.widget = WidgetEnvironment() self.widget.button_name = self.button_name self.spinner = NavigatorSpinner(parent=self.widget, total_width=20) # Widget setup self.button_name.setDefault(True) self.widget.setFocusPolicy(Qt.NoFocus) self.button_name.setFocusPolicy(Qt.StrongFocus) self.button_name.setToolTip(prefix if prefix else '') # Layouts layout = self._get_env_item_layout() self.widget.setLayout(layout) self.setSizeHint(self.widget.sizeHint()) def _get_env_item_layout(self): layout = QHBoxLayout() layout.addWidget(self.button_name) layout.addStretch() return layout def ordered_widgets(self): """Return a list of the ordered widgets.""" return [self.button_name] @property def name(self): """Resturn the environment name.""" return self._name @property def prefix(self): """Return the environment prefix.""" return self._prefix def set_hovered(self, value): """Set widget as hovered.""" self.widget.setProperty('hovered', value) self.button_name.set_hovered(value) def set_selected(self, value): """Set widget as selected.""" self._selected = value try: self.widget.setProperty('pressed', value) self.button_name.set_selected(value) except RuntimeError: pass self.button_name.setDisabled(value) @classmethod def widget_size(cls): """Return the size defined in the SASS file.""" return QSize(cls.ENV_ITEM_WIDTH, cls.ENV_ITEM_HEIGHT) class ListWidgetEnv(BaseListWidgetEnv): """Custom list widget environments.""" def setup_item(self, item): """Add additional logic after adding an item.""" super().setup_item(item) item.button_options.sig_entered.connect(lambda v=None: self.scroll_to_item(item)) class ListItemEnv(BaseListItemEnv): """Widget to build an item for the environments list.""" def __init__(self, name=None, prefix=None): """Widget to build an item for the environments list.""" super().__init__(name=name, prefix=prefix) def _get_env_item_layout(self): self.button_options = ButtonEnvironmentOptions() self.widget.button_options = self.button_options # pylint: disable=attribute-defined-outside-init self.button_options.setFocusPolicy(Qt.StrongFocus) self.frame_icon = FrameEnvironmentIcon() lay = QHBoxLayout() lay.addWidget(self.spinner) self.frame_icon.setLayout(lay) layout = QHBoxLayout() layout.addWidget(self.frame_icon) layout.addWidget(self.button_name) layout.addStretch() layout.addWidget(self.button_options) layout.addSpacing(16) return layout def ordered_widgets(self): """Return a list of the ordered widgets.""" return [self.button_name, self.button_options] def set_loading(self, value): """Set loading status of widget.""" if value: self.spinner.start() else: self.spinner.stop() def set_selected(self, value): """Set widget as selected.""" super().set_selected(value) self.spinner.stop() if value: self.button_options.setVisible(True) else: self.button_options.setVisible(False) # --- Local testing # ----------------------------------------------------------------------------- def print_selected(item): """Print selected item name.""" print('Item selected: ', item.name if item else 'None') def local_test(): # pragma: no cover """Run local test.""" app = qapplication() widget = ListWidgetEnv() widget.sig_item_selected.connect(print_selected) widget.addItem(ListItemEnv('root')) item = ListItemEnv('extralongnamewithsome1212331231321233') widget.addItem(item) for i in range(5): item = ListItemEnv('env ' + str(i)) widget.addItem(item) widget.update_style_sheet() widget.show() sys.exit(app.exec_()) if __name__ == '__main__': # pragma: no cover local_test()