diff --git a/enaml/qt/docking/q_bitmap_button.py b/enaml/qt/docking/q_bitmap_button.py new file mode 100644 index 000000000..155421dc9 --- /dev/null +++ b/enaml/qt/docking/q_bitmap_button.py @@ -0,0 +1,129 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2013, Nucleic Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#------------------------------------------------------------------------------ +from PyQt4.QtCore import QPoint, QRect +from PyQt4.QtGui import QAbstractButton, QColor, QPainter, QStyle, QStyleOption + + +class QBitmapButton(QAbstractButton): + """ A button widget which renders a bitmap. + + This class is used to render the various maximize, restore, and + close buttons in the docking framework. Bitmap images are chosen + for rendering so that the button can be fully styled using Qt + style sheets. + + """ + _bitmap = None + + def bitmap(self): + """ Get the bitmap associated with the button. + + """ + return self._bitmap + + def setBitmap(self, bitmap): + """ Set the bitmap associate with the button. + + """ + self._bitmap = bitmap + self.update() + + def sizeHint(self): + """ Get the size hint for the bitmap button. + + The size hint of the button is equal to it's icon size. + + """ + return self.minimumSizeHint() + + def minimumSizeHint(self): + """ Get the minimum size hint for the bitmap button. + + The minimum size hint of the button is equal to it's icon size. + + """ + return self.iconSize() + + def enterEvent(self, event): + """ Handle the enter event for the button. + + """ + if self.isEnabled(): + self.update() + super(QBitmapButton, self).enterEvent(event) + + def leaveEvent(self, event): + """ Handle the leave event for the button. + + """ + if self.isEnabled(): + self.update() + super(QBitmapButton, self).leaveEvent(event) + + def styleOption(self): + """ Get a filled style option for the button. + + Returns + ------- + result : QStyleOption + A style option initialized for the current button state. + + """ + opt = QStyleOption() + opt.initFrom(self) + opt.state |= QStyle.State_AutoRaise + is_down = self.isDown() + is_enabled = self.isEnabled() + is_checked = self.isChecked() + under_mouse = self.underMouse() + if is_enabled and under_mouse and not is_checked and not is_down: + opt.state |= QStyle.State_Raised + if is_checked: + opt.state |= QStyle.State_On + if is_down: + opt.state |= QStyle.State_Sunken + return opt + + def drawBitmap(self, opt, painter): + """ Draw the bitmap for the button. + + The bitmap will be drawn with the foreground color set by + the style sheet and the style option. + + Parameters + ---------- + opt : QStyleOption + The style option to use for drawing. + + painter : QPainter + The painter to use for drawing. + + """ + bmp = self._bitmap + if bmp is not None: + # hack to get the current stylesheet foreground color + hint = QStyle.SH_GroupBox_TextLabelColor + fg = self.style().styleHint(hint, opt, self) + # mask signed to unsigned which 'fromRgba' requires + painter.setPen(QColor.fromRgba(0xffffffff & fg)) + size = self.size() + im_size = bmp.size() + x = size.width() / 2 - im_size.width() / 2 + y = size.height() / 2 - im_size.height() / 2 + source = QRect(QPoint(0, 0), im_size) + dest = QRect(QPoint(x, y), im_size) + painter.drawPixmap(dest, bmp, source) + + def paintEvent(self, event): + """ Handle the paint event for the button. + + """ + painter = QPainter(self) + opt = self.styleOption() + self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self) + self.drawBitmap(opt, painter) diff --git a/enaml/qt/docking/q_dock_tab_widget.py b/enaml/qt/docking/q_dock_tab_widget.py index 15c088fc1..a8093e839 100644 --- a/enaml/qt/docking/q_dock_tab_widget.py +++ b/enaml/qt/docking/q_dock_tab_widget.py @@ -5,11 +5,36 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from PyQt4.QtCore import Qt, QPoint, QMetaObject, QEvent +from PyQt4.QtCore import Qt, QPoint, QSize, QMetaObject, QEvent from PyQt4.QtGui import ( - QApplication, QTabBar, QTabWidget, QMouseEvent, QResizeEvent + QApplication, QTabBar, QTabWidget, QMouseEvent, QResizeEvent, QStyle ) +from .q_bitmap_button import QBitmapButton +from .xbms import CLOSE_BUTTON + + +class QDockTabCloseButton(QBitmapButton): + """ A bitmap button subclass used as a dock tab close button. + + """ + def styleOption(self): + """ Get a filled style option for the button. + + Returns + ------- + result : QStyleOption + A style option initialized for the current button state. + + """ + opt = super(QDockTabCloseButton, self).styleOption() + parent = self.parent() + if isinstance(parent, QDockTabBar): + index = parent.currentIndex() + if parent.tabButton(index, QTabBar.RightSide) is self: + opt.state |= QStyle.State_Selected + return opt + class QDockTabBar(QTabBar): """ A custom QTabBar that manages safetly undocking a tab. @@ -59,25 +84,47 @@ def setCloseButtonVisible(self, index, visible): # A workaround is to send a dummy resize event. button.setVisible(visible) if not visible: - button.resize(0, 0) + button.resize(0, 0) else: - button.resize(button.sizeHint()) + button.resize(button.sizeHint()) size = self.size() event = QResizeEvent(size, size) QApplication.sendEvent(self, event) self.update() + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + def _onCloseButtonClicked(self): + """ Handle the 'clicked' signal on the tab close buttons. + + This handler will find the tab index for the clicked button + and emit the 'tabCloseRequested' signal with that index. + + """ + button = self.sender() + for index in xrange(self.count()): + if self.tabButton(index, QTabBar.RightSide) is button: + self.tabCloseRequested.emit(index) + #-------------------------------------------------------------------------- # Reimplementations #-------------------------------------------------------------------------- def tabInserted(self, index): """ Handle a tab insertion in the tab bar. - This handler will update the visibilty of close button for - the inserted tab. This method assumes that this tab bar is - properly parented by a QDockTabWidget. + This handler will create the close button for the tab and then + update its visibilty depending on whether or not the dock item + is closable. This method assumes that this tab bar is parented + by a QDockTabWidget. """ + button = QDockTabCloseButton(self) + button.setObjectName("docktab-close-button") + button.setBitmap(CLOSE_BUTTON.toBitmap()) + button.setIconSize(QSize(14, 13)) + button.clicked.connect(self._onCloseButtonClicked) + self.setTabButton(index, QTabBar.RightSide, button) visible = self.parent().widget(index).closable() self.setCloseButtonVisible(index, visible) diff --git a/enaml/qt/docking/q_dock_title_bar.py b/enaml/qt/docking/q_dock_title_bar.py index 0c10ec813..ee89822fd 100644 --- a/enaml/qt/docking/q_dock_title_bar.py +++ b/enaml/qt/docking/q_dock_title_bar.py @@ -6,14 +6,12 @@ # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ from PyQt4.QtCore import QSize, QMargins, pyqtSignal -from PyQt4.QtGui import QWidget, QFrame, QHBoxLayout, QIcon +from PyQt4.QtGui import QWidget, QFrame, QHBoxLayout -from .q_icon_button import QIconButton +from .q_bitmap_button import QBitmapButton from .q_icon_widget import QIconWidget from .q_text_label import QTextLabel - -# Make sure the resources get registered. -from . import dock_resources +from .xbms import CLOSE_BUTTON, MAXIMIZE_BUTTON, RESTORE_BUTTON class IDockTitleBar(QWidget): @@ -163,35 +161,23 @@ def __init__(self, parent=None): title_label = self._title_label = QTextLabel(self) - max_icon = QIcon() - max_icon.addFile(':dock_images/maxbtn_s.png') - max_icon.addFile(':dock_images/maxbtn_h.png', mode=QIcon.Active) - max_icon.addFile(':dock_images/maxbtn_p.png', mode=QIcon.Selected) - - restore_icon = QIcon() - restore_icon.addFile(':dock_images/rstrbtn_s.png') - restore_icon.addFile(':dock_images/rstrbtn_h.png', mode=QIcon.Active) - restore_icon.addFile(':dock_images/rstrbtn_p.png', mode=QIcon.Selected) - - close_icon = QIcon() - close_icon.addFile(':dock_images/closebtn_s.png') - close_icon.addFile(':dock_images/closebtn_h.png', mode=QIcon.Active) - close_icon.addFile(':dock_images/closebtn_p.png', mode=QIcon.Selected) - btn_size = QSize(14, 13) - max_button = self._max_button = QIconButton(self) - max_button.setIcon(max_icon) + max_button = self._max_button = QBitmapButton(self) + max_button.setObjectName('docktitlebar-maximize-button') + max_button.setBitmap(MAXIMIZE_BUTTON.toBitmap()) max_button.setIconSize(btn_size) max_button.setVisible(self._buttons & self.MaximizeButton) - restore_button = self._restore_button = QIconButton(self) - restore_button.setIcon(restore_icon) + restore_button = self._restore_button = QBitmapButton(self) + restore_button.setObjectName('docktitlebar-restore-button') + restore_button.setBitmap(RESTORE_BUTTON.toBitmap()) restore_button.setIconSize(btn_size) restore_button.setVisible(self._buttons & self.RestoreButton) - close_button = self._close_button = QIconButton(self) - close_button.setIcon(close_icon) + close_button = self._close_button = QBitmapButton(self) + close_button.setObjectName('docktitlebar-close-button') + close_button.setBitmap(CLOSE_BUTTON.toBitmap()) close_button.setIconSize(btn_size) close_button.setVisible(self._buttons & self.CloseButton) diff --git a/enaml/qt/docking/q_dock_window.py b/enaml/qt/docking/q_dock_window.py index d5d67a849..7cf0996f4 100644 --- a/enaml/qt/docking/q_dock_window.py +++ b/enaml/qt/docking/q_dock_window.py @@ -5,18 +5,16 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from PyQt4.QtCore import Qt, QMargins, QPoint, QRect, pyqtSignal -from PyQt4.QtGui import QFrame, QHBoxLayout, QIcon, QLayout +from PyQt4.QtCore import Qt, QMargins, QPoint, QRect, QSize, pyqtSignal +from PyQt4.QtGui import QFrame, QHBoxLayout, QLayout from atom.api import Bool, Typed +from .q_bitmap_button import QBitmapButton from .q_dock_area import QDockArea from .q_dock_frame import QDockFrame from .q_dock_frame_layout import QDockFrameLayout -from .q_icon_button import QIconButton - -# Make sure the resources get registered. -from . import dock_resources +from .xbms import CLOSE_BUTTON, MAXIMIZE_BUTTON, RESTORE_BUTTON #: The maximum number of free windows to keep in the free list. @@ -64,37 +62,22 @@ def __init__(self, parent=None): super(QDockWindowButtons, self).__init__(parent) self._buttons = self.CloseButton | self.MaximizeButton - hovered = QIcon.Active - pressed = QIcon.Selected - - max_icon = QIcon() - max_icon.addFile(':dock_images/maxbtn_large_s.png') - max_icon.addFile(':dock_images/maxbtn_large_h.png', mode=hovered) - max_icon.addFile(':dock_images/maxbtn_large_p.png', mode=pressed) - - restore_icon = QIcon() - restore_icon.addFile(':dock_images/rstrbtn_large_s.png') - restore_icon.addFile(':dock_images/rstrbtn_large_h.png', mode=hovered) - restore_icon.addFile(':dock_images/rstrbtn_large_p.png', mode=pressed) - - close_icon = QIcon() - close_icon.addFile(':dock_images/closebtn_large_s.png') - close_icon.addFile(':dock_images/closebtn_large_h.png', mode=hovered) - close_icon.addFile(':dock_images/closebtn_large_p.png', mode=pressed) - - max_button = self._max_button = QIconButton(self) - max_button.setIcon(max_icon) - max_button.setIconSize(max_icon.availableSizes()[0]) + max_button = self._max_button = QBitmapButton(self) + max_button.setObjectName("dockwindow-maximize-button") + max_button.setBitmap(MAXIMIZE_BUTTON.toBitmap()) + max_button.setIconSize(QSize(20, 15)) max_button.setVisible(self._buttons & self.MaximizeButton) - restore_button = self._restore_button = QIconButton(self) - restore_button.setIcon(restore_icon) - restore_button.setIconSize(restore_icon.availableSizes()[0]) + restore_button = self._restore_button = QBitmapButton(self) + restore_button.setObjectName("dockwindow-restore-button") + restore_button.setBitmap(RESTORE_BUTTON.toBitmap()) + restore_button.setIconSize(QSize(20, 15)) restore_button.setVisible(self._buttons & self.RestoreButton) - close_button = self._close_button = QIconButton() - close_button.setIcon(close_icon) - close_button.setIconSize(close_icon.availableSizes()[0]) + close_button = self._close_button = QBitmapButton(self) + close_button.setObjectName("dockwindow-close-button") + close_button.setBitmap(CLOSE_BUTTON.toBitmap()) + close_button.setIconSize(QSize(34, 15)) close_button.setVisible(self._buttons & self.CloseButton) layout = QHBoxLayout() diff --git a/enaml/qt/docking/q_icon_button.py b/enaml/qt/docking/q_icon_button.py deleted file mode 100644 index 2893fc459..000000000 --- a/enaml/qt/docking/q_icon_button.py +++ /dev/null @@ -1,60 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file COPYING.txt, distributed with this software. -#------------------------------------------------------------------------------ -import sys - -from PyQt4.QtCore import QSize -from PyQt4.QtGui import QAbstractButton, QPainter, QIcon - - -class QIconButton(QAbstractButton): - """ A button widget which renders tightly to its icon. - - """ - def sizeHint(self): - """ Get the size hint for the icon button. - - """ - return self.minimumSizeHint() - - def minimumSizeHint(self): - """ Get the minimum size hint for the icon button. - - """ - left, top, right, bottom = self.getContentsMargins() - return self.iconSize() + QSize(left + right, top + bottom) - - def enterEvent(self, event): - """ Handle the enter event for the button. - - """ - super(QIconButton, self).enterEvent(event) - if sys.platform == 'darwin': - self.repaint() - - def leaveEvent(self, event): - """ Handle the leave event for the button. - - """ - super(QIconButton, self).leaveEvent(event) - if sys.platform == 'darwin': - self.repaint() - - def paintEvent(self, event): - """ Handle the paint event for the button. - - """ - icon = self.icon() - if icon.isNull(): - return - if self.isDown(): - mode = QIcon.Selected - elif self.underMouse(): - mode = QIcon.Active - else: - mode = QIcon.Normal - icon.paint(QPainter(self), self.contentsRect(), mode=mode) diff --git a/enaml/qt/docking/xbms.py b/enaml/qt/docking/xbms.py new file mode 100644 index 000000000..7c06196b3 --- /dev/null +++ b/enaml/qt/docking/xbms.py @@ -0,0 +1,96 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2013, Nucleic Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#------------------------------------------------------------------------------ +from atom.api import Atom, Int, Str + + +class XBM(Atom): + """ A simple class representing an XMB image. + + """ + #: The width of the xbm image. + width = Int() + + #: The height of the xbm image. + height = Int() + + #: The bytestring of image data. + data = Str() + + def __init__(self, width, height, data): + """ Initialize an XBM image. + + Parameters + ---------- + width : int + The width of the bitmap. + + height : int + The height of the bitmap. + + data : list + A list of 1s and 0s which represent the bitmap data. + The length must be equal to width * height. + + """ + assert len(data) == (width * height) + bytes = [] + for row in xrange(height): + val = 0 + offset = row * width + for col in xrange(width): + d = col % 8 + if col > 0 and d == 0: + bytes.append(chr(val)) + val = 0 + v = data[offset + col] + val |= v << (7 - d) + bytes.append(chr(val)) + self.width = width + self.height = height + self.data = ''.join(bytes) + + def toBitmap(self): + from PyQt4.QtCore import QSize + from PyQt4.QtGui import QBitmap, QImage + size = QSize(self.width, self.height) + return QBitmap.fromData(size, self.data, QImage.Format_Mono) + + +CLOSE_BUTTON = XBM(8, 7, [ + 1, 1, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 1, 1, +]) + + +MAXIMIZE_BUTTON = XBM(8, 7, [ + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +]) + + +RESTORE_BUTTON = XBM(10, 9, [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, +]) diff --git a/enaml/qt/qt_dock_area.py b/enaml/qt/qt_dock_area.py index aa3dbea0e..eab7070da 100644 --- a/enaml/qt/qt_dock_area.py +++ b/enaml/qt/qt_dock_area.py @@ -5,8 +5,6 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from textwrap import dedent - from PyQt4.QtCore import QObject, QEvent, QSize, QTimer from PyQt4.QtGui import QTabWidget @@ -28,128 +26,6 @@ } -def make_style_sheet(style): - """ A function to generate the stylesheet for the give style. - - """ - - if style is None: - return '' - bg = ' background: rgba({0.red}, {0.green}, {0.blue}, {0.alpha});' - fg = ' color: rgba({0.red}, {0.green}, {0.blue}, {0.alpha});' - bd = ' border: 1px solid rgba({0.red}, {0.green}, {0.blue}, {0.alpha});' - items = {} - parts = [] - push = parts.append - def get(attr, which): - if getattr(style, attr) is not None: - items[attr] = which.format(getattr(style, attr)) - def push_item(attr): - push(items[attr]) - def push_item_if(attr): - if attr in items: - push(items[attr]) - get('dock_area_background', bg) - get('splitter_handle_background', bg) - get('dock_window_background', bg) - get('dock_window_border', bd) - get('dock_container_background', bg) - get('dock_container_border', bd) - get('dock_item_background', bg) - get('title_bar_background', bg) - get('title_bar_foreground', fg) - get('tab_background', bg) - get('tab_hover_background', bg) - get('tab_selected_background', bg) - get('tab_foreground', fg) - get('tab_hover_foreground', fg) - get('tab_selected_foreground', fg) - push('QDockArea {') - push(' padding: 5px;') - push_item_if('dock_area_background') - push('}') - if 'splitter_handle_background' in items: - push('QDockSplitterHandle {') - push_item('splitter_handle_background') - push('}') - if 'dock_window_background' in items or 'dock_window_border' in items: - push('QDockWindow {') - push_item_if('dock_window_background') - push_item_if('dock_window_border') - push('}') - if 'dock_container_background' in items: - push('QDockContainer {') - push_item('dock_container_background') - push('}') - if 'dock_container_border' in items: - push('QDockContainer[floating="true"] {') - push_item('dock_container_border') - push('}') - if 'dock_item_background' in items: - push('QDockItem {') - push_item('dock_item_background') - push('}') - if 'title_bar_background' in items: - push('QDockTitleBar {') - push_item('title_bar_background') - push('}') - if 'title_bar_foreground' in items or 'title_bar_font' in items: - push('QDockTitleBar > QTextLabel {') - push_item_if('title_bar_foreground') - # push_item_if('title_bar_font') - push('}') - push(dedent("""\ - /* Correct a bug in the pane size when using system styling */ - QDockTabWidget::pane { - } - QDockTabBar::close-button { - margin-bottom: 2px; - image: url(:dock_images/closebtn_s.png); - } - QDockTabBar::close-button:selected { - image: url(:dock_images/closebtn_b.png); - } - QDockTabBar::close-button:hover, - QDockTabBar::close-button:selected:hover { - image: url(:dock_images/closebtn_h.png); - } - QDockTabBar::close-button:pressed, - QDockTabBar::close-button:selected:pressed { - image: url(:dock_images/closebtn_p.png); - }""")) - if 'tab_background' in items or 'tab_foreground' in items: - push('QDockTabBar::tab {') - push_item_if('tab_background') - push_item_if('tab_foreground') - push('}') - push(dedent("""\ - QDockTabBar::tab:top, QDockTabBar::tab:bottom { - margin-right: 1px; - padding-left: 5px; - padding-right: 5px; - padding-bottom: 2px; - height: 17px; - } - - QDockTabBar::tab:left, QDockTabBar::tab:right { - margin-bottom: 1px; - padding-top: 5px; - padding-bottom: 5px; - width: 20px; - }""")) - if 'tab_hover_background' in items or 'tab_hover_foreground' in items: - push('QDockTabBar::tab:hover {') - push_item_if('tab_hover_background') - push_item_if('tab_hover_foreground') - push('}') - if 'tab_selected_background' in items or 'tab_selected_foreground' in items: - push('QDockTabBar::tab:selected {') - push_item_if('tab_selected_background') - push_item_if('tab_selected_foreground') - push('}') - return '\n'.join(parts) - - class DockFilter(QObject): """ A simple event filter used by the QtDockArea. @@ -215,7 +91,8 @@ def init_widget(self): super(QtDockArea, self).init_widget() d = self.declaration self.set_tab_position(d.tab_position) - self.set_style(d.style) + if d.style_sheet: + self.set_style_sheet(d.style_sheet) def init_layout(self): """ Initialize the layout of the underlying control. @@ -285,11 +162,11 @@ def set_tab_position(self, position): """ self.widget.setTabPosition(TAB_POSITIONS[position]) - def set_style(self, style): - """ Set the style for the underlying widget. + def set_style_sheet(self, style_sheet): + """ Set the style sheet for the underlying widget. """ - self.widget.setStyleSheet(make_style_sheet(style)) + self.widget.setStyleSheet(style_sheet) def save_layout(self): """ Save the current layout on the underlying widget. diff --git a/enaml/qt/qt_object_combo.py b/enaml/qt/qt_object_combo.py index c45d10a51..4060776c3 100644 --- a/enaml/qt/qt_object_combo.py +++ b/enaml/qt/qt_object_combo.py @@ -137,7 +137,7 @@ def refresh_items(self): widget.addItem(text) else: widget.addItem(qicon, text) - if item is selected: + if item == selected: target_index = index widget.setCurrentIndex(target_index) finally: diff --git a/enaml/widgets/api.py b/enaml/widgets/api.py index 79ec3c705..fc22f2630 100644 --- a/enaml/widgets/api.py +++ b/enaml/widgets/api.py @@ -14,9 +14,8 @@ from .date_selector import DateSelector from .datetime_selector import DatetimeSelector from .dock_area import DockArea -from .dock_area_style import ( - DockAreaStyle, vs_2010_style, grey_wind_style, new_moon_style, - daydreamer_style, metro_style +from .dock_area_styles import ( + VS_2010_STYLE, GREY_WIND_STYLE, NEW_MOON_STYLE, METRO_STYLE ) from .dock_item import DockItem from .dock_pane import DockPane diff --git a/enaml/widgets/dock_area.py b/enaml/widgets/dock_area.py index 791458522..d048b52c3 100644 --- a/enaml/widgets/dock_area.py +++ b/enaml/widgets/dock_area.py @@ -5,7 +5,9 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ -from atom.api import Coerced, Enum, Typed, ForwardTyped, observe, set_default +from atom.api import ( + Coerced, Enum, Typed, ForwardTyped, Unicode, observe, set_default +) from enaml.core.declarative import d_ from enaml.layout.dock_layout import ( @@ -13,7 +15,7 @@ ) from .constraints_widget import ConstraintsWidget, ProxyConstraintsWidget -from .dock_area_style import DockAreaStyle, vs_2010_style +from .dock_area_styles import VS_2010_STYLE from .dock_item import DockItem @@ -48,7 +50,7 @@ class ProxyDockArea(ProxyConstraintsWidget): def set_tab_position(self, position): raise NotImplementedError - def set_style(self, style): + def set_style_sheet(self, style_sheet): raise NotImplementedError def save_layout(self): @@ -77,7 +79,7 @@ class DockArea(ConstraintsWidget): #: The style to apply to the dock area. The default style resembles #: Visual Studio 2010. - style = d_(Typed(DockAreaStyle, factory=vs_2010_style)) + style_sheet = d_(Unicode(VS_2010_STYLE)) #: A Stack expands freely in height and width by default hug_width = set_default('ignore') @@ -202,7 +204,7 @@ def _update_layout(self, change): if change['type'] == 'update': self.apply_layout(change['value']) - @observe(('tab_position', 'style')) + @observe(('tab_position', 'style_sheet')) def _update_proxy(self, change): """ Update the proxy when the area state changes. diff --git a/enaml/widgets/dock_area_style.py b/enaml/widgets/dock_area_style.py deleted file mode 100644 index ab4fe3fbe..000000000 --- a/enaml/widgets/dock_area_style.py +++ /dev/null @@ -1,194 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2013, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file COPYING.txt, distributed with this software. -#------------------------------------------------------------------------------ -from atom.api import Atom - -from enaml.colors import ColorMember, Color -from enaml.fonts import FontMember - - -class DockAreaStyle(Atom): - """ A class used to define the style to apply to the dock area. - - The developer is not required to explicitly specify all of the - style values. The toolkit will apply an appropriate default for - any value which is not explicitly provided. - - """ - #: The background color of a dock area. - dock_area_background = ColorMember() - - #: The background color of a splitter handle. - splitter_handle_background = ColorMember() - - #: The background color of a dock windows. - dock_window_background = ColorMember() - - #: The border color of a dock window. - dock_window_border = ColorMember() - - #: The background color of a dock container. - dock_container_background = ColorMember() - - #: The border color of a floating dock container. - dock_container_border = ColorMember() - - #: The background color of a dock item. - dock_item_background = ColorMember() - - #: The background color of a dock item title bar. - title_bar_background = ColorMember() - - #: The foreground color of a dock item title bar. - title_bar_foreground = ColorMember() - - #: The font of a dock item title bar. - title_bar_font = FontMember() - - #: The background color of a tab in a dock tab bar. - tab_background = ColorMember() - - #: The background color of a hovered tab in a dock tab bar. - tab_hover_background = ColorMember() - - #: The background color of a selected tab in a dock tab bar. - tab_selected_background = ColorMember() - - #: The foreground color of a tab in the dock tab bar. - tab_foreground = ColorMember() - - #: The foreground color of a hovered tab in a dock tab bar. - tab_hover_foreground = ColorMember() - - #: The foreground color of a selected tab in a dock tab bar. - tab_selected_foreground = ColorMember() - - -def vs_2010_style(): - """ A style which is inspired by Visual Studio 2010. - - """ - return DockAreaStyle( - dock_area_background=Color(49, 67, 98), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(53, 73, 106), - dock_window_border=Color(40, 60, 90), - dock_container_background=Color(53, 73, 106), - dock_container_border=Color(40, 60, 90), - dock_item_background=Color(240, 240, 240), - title_bar_background=Color(77, 96, 130), - title_bar_foreground=Color(250, 251, 254), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 15), - tab_hover_background=Color(76, 105, 153), - tab_selected_background=Color(240, 240, 240), - tab_foreground=Color(250, 251, 254), - tab_selected_foreground=Color(0, 0, 0), - ) - - -def grey_wind_style(): - """ A mild grey and brown color scheme. - - Inspired by: - http://www.colourlovers.com/palette/2866138/Grey_Wind - - """ - return DockAreaStyle( - dock_area_background=Color(139, 131, 129), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(175, 182, 190), - dock_window_border=Color(144, 144, 152), - dock_container_background=Color(175, 178, 183), - dock_container_border=Color(144, 144, 152), - dock_item_background=Color(244, 244, 244), - title_bar_background=Color(144, 144, 152), - title_bar_foreground=Color(244, 244, 244), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 35), - tab_foreground=Color(244, 244, 244), - tab_hover_background=Color(193, 191, 196), - tab_hover_foreground=Color(70, 70, 70), - tab_selected_background=Color(244, 244, 244), - tab_selected_foreground=Color(0, 0, 0), - ) - - -def new_moon_style(): - """ A yellow, brown, and grey scheme which has lights and darks. - - Inspired by: - http://www.colourlovers.com/palette/90734/Newly_Risen_Moon - - """ - return DockAreaStyle( - dock_area_background=Color(54, 57, 59), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(197, 188, 142), - dock_window_border=Color(105, 103, 88), - dock_container_background=Color(62, 72, 75), - dock_container_border=Color(54, 57, 59), - dock_item_background=Color(240, 240, 240), - title_bar_background=Color(105, 103, 88), - title_bar_foreground=Color(244, 244, 244), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 30), - tab_foreground=Color(244, 244, 244), - tab_hover_background=Color(197, 188, 142), - tab_selected_background=Color(240, 240, 240), - tab_selected_foreground=Color(0, 0, 0), - ) - - -def daydreamer_style(): - """ A tempered olive colored theme. - - Inspired by: - http://www.colourlovers.com/palette/590207/d_a_y_d_r_e_a_m_e_r - - """ - return DockAreaStyle( - dock_area_background=Color(168, 168, 120), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(131, 144, 116), - dock_window_border=Color(6, 16, 19), - dock_container_background=Color(147, 158, 120), - dock_container_border=Color(6, 16, 19), - dock_item_background=Color(244, 244, 244), - title_bar_background=Color(131, 144, 116), - title_bar_foreground=Color(250, 250, 250), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(255, 255, 255, 35), - tab_foreground=Color(244, 244, 244), - tab_hover_background=Color(205, 205, 118), - tab_selected_background=Color(244, 244, 244), - tab_selected_foreground=Color(6, 16, 19), - ) - - -def metro_style(): - """ A style which is inspired by Windows Metro UIs. - - """ - return DockAreaStyle( - dock_area_background=Color(0xca, 0xca, 0xca), - splitter_handle_background=Color(0, 0, 0, 0), - dock_window_background=Color(255, 255, 255), - dock_window_border=Color(0x66, 0x66, 0x66), - dock_container_background=Color(0xca, 0xca, 0xca), - dock_container_border=Color(0x66, 0x66, 0x66), - dock_item_background=Color(240, 240, 240), - title_bar_background=Color(53, 139, 202), - title_bar_foreground=Color(240, 240, 240), - title_bar_font='9pt "Segoe UI"', - tab_background=Color(0x66, 0x66, 0x66), - tab_foreground=Color(240, 240, 240), - tab_hover_background=Color(0xff, 0xfb, 0x85, 200), - tab_hover_foreground=Color(0x33, 0x33, 0x33), - tab_selected_background=Color(240, 240, 240), - tab_selected_foreground=Color(0x33, 0x33, 0x33), - ) diff --git a/enaml/widgets/dock_area_styles.py b/enaml/widgets/dock_area_styles.py new file mode 100644 index 000000000..85ebbcf90 --- /dev/null +++ b/enaml/widgets/dock_area_styles.py @@ -0,0 +1,493 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2013, Nucleic Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#------------------------------------------------------------------------------ + + +#: A stylesheet inspired by Visual Studio 2010 +VS_2010_STYLE = u""" + QDockArea { + padding: 5px; + background: rgb(49, 67, 98); + border: 1px solid rgb(40, 60, 90); + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: rgb(53, 73, 106); + border: 1px solid rgb(40, 60, 90); + } + + QDockContainer { + background: rgb(53, 73, 106); + } + + QDockContainer[floating="true"] { + border: 1px solid rgb(40, 60, 90); + } + + QDockItem { + background: rgb(240, 240, 240); + } + + QDockTitleBar { + background: rgb(77, 96, 130); + } + + QDockTitleBar > QTextLabel { + color: rgb(250, 251, 254); + font: 9pt "Segoe UI"; + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar { + font: 9pt "Segoe UI"; + } + + QDockTabBar::tab { + background: rgba(255, 255, 255, 15); + color: rgb(250, 251, 254); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgba(255, 255, 255, 70); + } + + QDockTabBar::tab:selected { + background: rgb(240, 240, 240); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton, + QDockWindowButtons QBitmapButton { + color: rgb(250, 251, 254); + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: #3665B3; + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: #3D6099; + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + border: 1px solid rgb(229, 195, 101); + background: rgb(250, 251, 254); + color: black; + } + + QDockTabBar QBitmapButton:pressed, + QDockTitleBar QBitmapButton:pressed { + background: rgb(255, 229, 128); + } + + QBitmapButton#docktab-close-button:selected { + color: black; + } +""" + +#: A mild grey and brown stylesheet. +#: Inspired by http://www.colourlovers.com/palette/2866138/Grey_Wind +GREY_WIND_STYLE = u""" + QDockArea { + padding: 5px; + background: rgb(175, 178, 183); + border: 1px solid rgb(161, 164, 168); + } + + QDockWindow > QDockArea { + border: 1px solid rgb(129, 121, 119); + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: rgb(139, 131, 129); + border: 1px solid rgb(129, 121, 119); + } + + QDockContainer { + background: rgb(175, 178, 183); + } + + QDockContainer[floating="true"] { + border: 1px solid rgb(144, 144, 152); + } + + QDockItem { + background: rgb(244, 244, 244); + } + + QDockTitleBar { + background: rgb(144, 144, 152); + } + + QDockTitleBar > QTextLabel { + color: rgb(244, 244, 244); + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar::tab { + background: rgba(0, 0, 0, 20); + color: rgb(244, 244, 244); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgb(144, 144, 152); + } + + QDockTabBar::tab:selected { + background: rgb(244, 244, 244); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton { + color: rgb(250, 251, 254); + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + color: rgb(80, 80, 80); + } + + QDockTabBar QBitmapButton:pressed, + QDockTitleBar QBitmapButton:pressed { + color: black; + } + + QBitmapButton#docktab-close-button:selected { + color: rgb(80, 80, 80); + } + + QBitmapButton#docktab-close-button:selected:hover { + color: black; + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + color: rgb(250, 251, 254); + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: rgb(175, 178, 183); + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: rgb(144, 144, 152); + } +""" + + +#: A yellow, brown, and grey stylesheet. +#: Inspired by http://www.colourlovers.com/palette/90734/Newly_Risen_Moon +NEW_MOON_STYLE = u""" + QDockArea { + padding: 5px; + background: rgb(54, 57, 59); + border: 1px solid rgb(45, 45, 45); + } + + QDockWindow > QDockArea { + border: 1px solid #9E935D; + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: rgb(197, 188, 142); + border: 1px solid #9E935D; + } + + QDockContainer { + background: rgb(54, 57, 59); + } + + QDockContainer[floating="true"] { + border: 1px solid rgb(45, 45, 45); + } + + QDockItem { + background: rgb(240, 240, 240); + } + + QDockTitleBar { + background: rgb(105, 103, 88); + } + + QDockTitleBar > QTextLabel { + color: rgb(240, 240, 240); + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar::tab { + background: rgba(255, 255, 255, 30); + color: rgb(240, 240, 240); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgba(197, 188, 142, 170); + } + + QDockTabBar::tab:selected { + background: rgb(240, 240, 240); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton { + color: rgb(240, 240, 240); + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + color: rgb(50, 50, 50); + } + + QDockTabBar QBitmapButton:pressed, + QDockTitleBar QBitmapButton:pressed { + color: black; + } + + QBitmapButton#docktab-close-button:selected { + color: rgb(100, 100, 100); + } + + QBitmapButton#docktab-close-button:selected:hover { + color: black; + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + color: rgb(240, 240, 240); + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: #9E935D; + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: rgb(105, 103, 88); + } +""" + + +#: A stylesheet inspired by Windows Metro. +METRO_STYLE = u""" + QDockArea { + padding: 5px; + background: #C0C0C0; + border: 1px solid #B0B0B0; + } + + QDockSplitterHandle { + background: rgba(0, 0, 0, 0); + } + + QDockWindow { + background: white; + border: 1px solid #B0B0B0; + } + + QDockContainer { + background: #C0C0C0; + } + + QDockContainer[floating="true"] { + border: 1px solid #B0B0B0; + } + + QDockItem { + background: rgb(240, 240, 240); + } + + QDockTitleBar { + background: rgb(53, 139, 202); + } + + QDockTitleBar > QTextLabel { + color: rgb(240, 240, 240); + font: 9pt "Segoe UI"; + } + + /* Correct a bug in the default pane sizing on Windows 7 */ + QDockTabWidget::pane {} + + QDockTabBar { + font: 9pt "Segoe UI"; + } + + QDockTabBar::tab { + background: #666666; + background: #838587; + color: rgb(240, 240, 240); + } + + QDockTabBar::tab:top, + QDockTabBar::tab:bottom { + margin-right: 1px; + padding-left: 5px; + padding-right: 5px; + height: 19px; + } + + QDockTabBar::tab:left, + QDockTabBar::tab:right { + margin-bottom: 1px; + padding-top: 5px; + padding-bottom: 5px; + width: 20px; + } + + QDockTabBar::tab:hover { + background: rgb(53, 139, 202); + } + + QDockTabBar::tab:selected { + background: rgb(240, 240, 240); + color: black; + } + + QDockTabBar QBitmapButton, + QDockTitleBar QBitmapButton { + color: rgb(240, 240, 240); + } + + QDockTabBar QBitmapButton:hover, + QDockTitleBar QBitmapButton:hover { + color: black; + } + + QBitmapButton#docktab-close-button:selected { + color: rgb(100, 100, 100); + } + + QBitmapButton#docktab-close-button:selected:hover { + color: black; + } + + QBitmapButton#dockwindow-close-button { + background: #C75050; + color: white; + } + + QBitmapButton#dockwindow-close-button:hover { + background: #E04343; + } + + QBitmapButton#dockwindow-close-button:pressed { + background: #993D3D; + } + + QBitmapButton#dockwindow-maximize-button:hover, + QBitmapButton#dockwindow-restore-button:hover { + background: #3665B3; + color: white; + } + + QBitmapButton#dockwindow-maximize-button:pressed, + QBitmapButton#dockwindow-restore-button:pressed { + background: #3D6099; + } +""" diff --git a/examples/widgets/dock_area.enaml b/examples/widgets/dock_area.enaml index 9924b3bb4..9e8cd12b0 100644 --- a/examples/widgets/dock_area.enaml +++ b/examples/widgets/dock_area.enaml @@ -22,11 +22,18 @@ from enaml.layout.api import ( ) from enaml.widgets.api import ( Window, Container, DockArea, DockItem, PushButton, Field, Html, Slider, - ObjectCombo, vs_2010_style, grey_wind_style, new_moon_style, - daydreamer_style, metro_style + ObjectCombo, VS_2010_STYLE, GREY_WIND_STYLE, NEW_MOON_STYLE, METRO_STYLE ) +STYLES = { + 'VS 2010': VS_2010_STYLE, + 'Grey Wind': GREY_WIND_STYLE, + 'New Moon': NEW_MOON_STYLE, + 'Metro': METRO_STYLE, +} + + enamldef MyDockArea(DockArea): layout = hdocksplit( vdocksplit('Item 1', 'Item 3', 'Item 5'), @@ -129,10 +136,7 @@ enamldef Main(Window): names.append(name) area.find('Item 1').split('left', *names) ObjectCombo: style_c: - items = [ - vs_2010_style, grey_wind_style, new_moon_style, - daydreamer_style, metro_style, - ] - to_string = lambda func: func.__name__.replace('_', ' ') + items = STYLES.keys() + selected = 'VS 2010' MyDockArea: area: - style << style_c.selected() + style_sheet << STYLES[style_c.selected]