class ColorChooser(QFrame): ''' Completion-like widget to quick select hexadecimal colors used in a document TODO Make cell/widget size configurable? ''' colorSelected = pyqtSignal(QColor) def __init__(self, parent): super(ColorChooser, self).__init__(parent) self.colors = KColorCells(self, 1, 1) self.colors.setAcceptDrags(False) self.colors.setEditTriggers(self.colors.NoEditTriggers) self.otherBtn = KPushButton(self) self.otherBtn.setText(i18nc('@action:button', '&Other...')) layout = QVBoxLayout(self) layout.addWidget(self.colors) layout.addWidget(self.otherBtn) self.setFocusPolicy(Qt.StrongFocus) self.setFrameShape(QFrame.Panel) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup); # Subscribe to observe widget events # - open KColorDialog on 'Other...' button click self.otherBtn.clicked.connect(self.show_color_dialog) # - select color by RMB click or ENTER on keyboard self.colors.cellActivated.connect(self.color_selected) self.installEventFilter(self) def setColors(self, colors): if len(colors): rows, columns = _calc_dimensions_for_items_count(len(colors)) else: self.hide() self.show_color_dialog(True) return self.colors.setColumnCount(columns) self.colors.setRowCount(rows) self.colors.setMinimumSize(20 * columns, 25 * rows) self.updateGeometry() self.adjustSize() self.colors.resizeColumnsToContents() self.colors.resizeRowsToContents() # Fill color cells for i, color in enumerate(colors): self.colors.setColor(i, color) _set_tooltips(rows, columns, self.colors) # Set tooltips for all valid color cells self.colors.setFocus() # Give a focus to widget self.show() # Show it! @pyqtSlot(bool) def show_color_dialog(self, f): '''Get color using KColorDialog''' # Preselect last used color color = QColor(kate.sessionConfiguration[_INSERT_COLOR_LCC]) result = KColorDialog.getColor(color) if result == KColorDialog.Accepted: # Did user press OK? self.emitSelectedColorHideSelf(color) @pyqtSlot(int, int) def color_selected(self, row, column): '''Smth has selected in KColorCells''' color = self.colors.item(row, column).data(Qt.BackgroundRole) #kate.kDebug('ColorUtils: activated row={}, column={}, color={}'.format(row, column, color)) self.emitSelectedColorHideSelf(color) def emitSelectedColorHideSelf(self, color): # Remember last selected color for future preselect kate.sessionConfiguration[_INSERT_COLOR_LCC] = color.name() self.colorSelected.emit(color) self.hide() def eventFilter(self, obj, event): '''Hide self on Esc key''' if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Escape: self.hide() return True return super(ColorChooser, self).eventFilter(obj, event) def moveAround(self, position): '''Smart positioning self around cursor''' self.colors.resizeColumnsToContents() self.colors.resizeRowsToContents() mwg = kate.mainWindow().geometry() wc = mwg.center() pos = mwg.topLeft() + position pos.setY(pos.y() + 40) if wc.y() < pos.y(): pos.setY(pos.y() - self.height()) if wc.x() < pos.x(): pos.setX(pos.x() - self.width()) self.move(pos)
class ColorChooser(QFrame): ''' Completion-like widget to quick select hexadecimal colors used in a document TODO Make cell/widget size configurable? ''' _INSERT_COLOR_LCC = 'insertColor:lastUsedColor' colorSelected = pyqtSignal(QColor) def __init__(self): super(ColorChooser, self).__init__(None) self.colors = KColorCells(self, 1, 1) self.colors.setAcceptDrags(False) self.colors.setEditTriggers(self.colors.NoEditTriggers) self.otherBtn = KPushButton(self) self.otherBtn.setText(i18nc('@action:button', '&Other...')) layout = QVBoxLayout(self) layout.addWidget(self.colors) layout.addWidget(self.otherBtn) self.setFocusPolicy(Qt.StrongFocus) self.setFrameShape(QFrame.Panel) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup) # Set default value for last used #color if not configured yet if ColorChooser._INSERT_COLOR_LCC not in kate.sessionConfiguration: kate.sessionConfiguration[ColorChooser._INSERT_COLOR_LCC] = '#ffffff' # Subscribe to observe widget events # - open KColorDialog on 'Other...' button click self.otherBtn.clicked.connect(self._show_color_dialog) # - select color by RMB click or ENTER on keyboard self.colors.cellActivated.connect(self._color_selected) # - self.colorSelected.connect(self._insert_color_into_active_document) self.installEventFilter(self) def __del__(self): mw = kate.mainInterfaceWindow() if mw: self.blockSignals(True) self.removeEventFilter(self) def updateColors(self, document): '''Scan a given document and collect unique colors Returns a list of QColor objects. ''' result = [] # Iterate over document's lines trying to find #colors for l in range(0, document.lines()): line = document.line(l) # Get the current line start = 0 # Set initial position to 0 (line start) while start < len(line): # Repeat 'till the line end start = line.find('#', start) # Try to find a '#' character (start of #color) if start == -1: # Did we found smth? break # No! Nothing to do... # Try to get a word right after the '#' char end = start + 1 for c in line[end:]: if not (c in string.hexdigits or c in string.ascii_letters): break end += 1 color_range = KTextEditor.Range(l, start, l, end) color_str = document.text(color_range) color = QColor(color_str) if color.isValid() and color not in result: result.append(color) kate.kDebug('ColorUtils: scan for #colors found {}'.format(color_str)) start = end self._set_colors(result) def moveAround(self, position): '''Smart positioning self around cursor''' self.colors.resizeColumnsToContents() self.colors.resizeRowsToContents() mwg = kate.mainWindow().geometry() wc = mwg.center() pos = mwg.topLeft() + position pos.setY(pos.y() + 40) if wc.y() < pos.y(): pos.setY(pos.y() - self.height()) if wc.x() < pos.x(): pos.setX(pos.x() - self.width()) self.move(pos) def emitSelectedColorHideSelf(self, color): # Remember last selected color for future preselect kate.sessionConfiguration[ColorChooser._INSERT_COLOR_LCC] = color.name() self.colorSelected.emit(color) self.hide() def eventFilter(self, obj, event): '''Hide self on Esc key''' if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Escape: self.hide() return True return super(ColorChooser, self).eventFilter(obj, event) def _set_colors(self, colors): if len(colors): rows, columns = ColorChooser._calc_dimensions_for_items_count(len(colors)) else: self.hide() self._show_color_dialog(True) return self.colors.setColumnCount(columns) self.colors.setRowCount(rows) self.colors.setMinimumSize(20 * columns, 25 * rows) self.updateGeometry() self.adjustSize() self.colors.resizeColumnsToContents() self.colors.resizeRowsToContents() # Fill color cells for i, color in enumerate(colors): self.colors.setColor(i, color) setTooltips(rows, columns, self.colors) # Set tooltips for all valid color cells self.colors.setFocus() # Give a focus to widget self.show() # Show it! @pyqtSlot(bool) def _show_color_dialog(self, f): '''Get color using KColorDialog''' # Preselect last used color color = QColor(kate.sessionConfiguration[ColorChooser._INSERT_COLOR_LCC]) result = KColorDialog.getColor(color) if result == KColorDialog.Accepted: # Did user press OK? self.emitSelectedColorHideSelf(color) @pyqtSlot(int, int) def _color_selected(self, row, column): '''Smth has selected in KColorCells''' color = self.colors.item(row, column).data(Qt.BackgroundRole) #kate.kDebug('ColorUtils: activated row={}, column={}, color={}'.format(row, column, color)) self.emitSelectedColorHideSelf(color) @pyqtSlot(QColor) def _insert_color_into_active_document(self, color): color_str = color.name() # Get it as color string #kate.kDebug('ColorUtils: selected color: {}'.format(color_str)) document = kate.activeDocument() view = kate.activeView() has_selection = view.selection() # Remember current selection state color_range = ColorChooser._get_color_range_under_cursor(view) document.startEditing() document.replaceText(color_range, color_str) # Replace selected/found range w/ a new text document.endEditing() # Select just entered #color, if something was selected before if has_selection: start_pos = color_range.start() view.setSelection(KTextEditor.Range(start_pos, len(color_str))) @staticmethod def _calc_dimensions_for_items_count(count): '''Recalculate rows/columns of table view for given items count''' rows = int(math.sqrt(count)) columns = int(count / rows) + int(bool(count % rows)) return (rows, columns) @staticmethod def _get_color_range_under_cursor(view): assert(view is not None) if view.selection(): # Some text selected, just use it as input... color_range = view.selectionRange() else: # If no selection, try to get a #color under cursor color_range = common.getBoundTextRangeSL( common.IDENTIFIER_BOUNDARIES - {'#'} , common.IDENTIFIER_BOUNDARIES , view.cursorPosition() , view.document() ) # Check if a word under cursor is a valid #color color = QColor(view.document().text(color_range)) if not color.isValid(): color_range = KTextEditor.Range(view.cursorPosition(), view.cursorPosition()) return color_range