Merge completion branch

Squashed commit of the following:

commit 8bbf7242e7ae3168a0e8652ad643c7c1fe8670d3
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 21:34:35 2014 +0100

    foo

commit cc9a4eed6e7ca9599bcc40b40f43614040711714
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 21:33:09 2014 +0100

    fixes

commit b84c9207f8596dac0f47bf72cd178f8e3ee9ebb7
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 21:27:48 2014 +0100

    Make titles bold

commit d2ef899f90036b05856deb178e61de5d961e1342
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 21:18:01 2014 +0100

    Make tab work properly

commit ccc407093e39f1964c8e3ff2900c66b28dc7fe56
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 16:19:57 2014 +0100

    Add FIXME, remove showhide

commit cbe6d0a2b0614df839c75f1815f939c11e4ae3ba
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 16:12:55 2014 +0100

    Use stylesheet for highlight

commit 7eeb6e79f17bc8e7ccc1ec90f474d1c2b1c01c1e
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 16:01:50 2014 +0100

    Make highlighting work

commit 4fed5b9f7291109fd1c669c61501861223364b7d
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 17:49:30 2014 +0100

    First attempt at marking

commit 27a3bda214b7fc7741b4c12d723ccdf4073c0d5e
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 17:14:48 2014 +0100

    Funny broken marking

commit 2798cf75dd295ace2a33295e9a64dd373b7fd669
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 16:25:36 2014 +0100

    Make completion and filtering work

commit b213d9724bc9f4bb36204bd1b2c4aaf62a454e11
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 16:07:14 2014 +0100

    Implement dynamic source models

commit c7951e6b4346a1702509bea6d3694cc50550c9ed
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 14:44:31 2014 +0100

    Add custom filtering

commit 37b100f3a1a1f5a90592e34b268c08df684b0df9
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 14:43:57 2014 +0100

    Add default roles in model

commit 1610423c1dd36c0adc35743a38e2c3a81f3641ef
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 14:43:36 2014 +0100

    Remove dataChanged

commit 467712630b00a9ca2407903bdba977b5e66dd2bc
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 13:57:18 2014 +0100

    Fix stuff

commit 00bb46e0ef46b9ae61652bde3ae10d3252263e46
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 11:51:34 2014 +0100

    First attempt at a real CommandCompletionModel

commit a8be022f80d7d35049d1ae3d8431702e7a407698
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 10:25:48 2014 +0100

    Adapt completion view to size

commit ad6bf69a4257880ce14b0590bcb5381c718bb744
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 09:17:48 2014 +0100

    Fix stuff

commit 41c1fbfa942bab0a1a0db35f6b745b691c774b22
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 09:16:09 2014 +0100

    Hide CompletionView by default

commit 2ac8596dada6c7e079807bfd698377a9d3ffd0af
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 09:13:11 2014 +0100

    File shuffling

commit f0c85d5bee30095324be354934f3379c1c6a5291
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 09:04:29 2014 +0100

    Cleanup, don't show completionview

commit 177ac97acb2d611992869f122669d95a8dadcdbb
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 07:16:37 2014 +0100

    Add FIXME for eliding

commit 6b234c58928e9a231ec35e5080a27d7a8affebc4
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 07:12:55 2014 +0100

    Add TextOption to TextDocument

commit 195e95459b54bbdb0545cb0e4db417ab641cea28
Author: Florian Bruhin <git@the-compiler.org>
Date:   Mon Jan 27 07:01:41 2014 +0100

    Resize QTreeView contents based on window size

commit 05b854bc812f0aabf5c6dcbdfa1dfd74ea495170
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sun Jan 26 01:19:28 2014 +0100

    Split files

commit ff433b561179a562494c7d3558336a917edc497f
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sun Jan 26 00:57:18 2014 +0100

    cleanup

commit bdb17de2fee65411b4dd1d0ad449329f0586f1e0
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sun Jan 26 00:15:17 2014 +0100

    Make text drawing work properly

commit d844152074738df88ed79c2e1efc4eec4b6e5734
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sat Jan 25 14:03:16 2014 +0100

    Split painting into functions

commit 585a9f6931a407117c4232ac14844d33233922f8
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sat Jan 25 13:50:52 2014 +0100

    Use QTextDocument, things are getting better

commit 96fef2114e68766590ed33fa92c6f2bbac8adb11
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sat Jan 25 13:08:58 2014 +0100

    Failed try to reimplement painting

commit d967285f24a7966674c8685e9e276e560e9cc066
Author: Florian Bruhin <git@the-compiler.org>
Date:   Sat Jan 25 11:15:07 2014 +0100

    Try drawing over text

commit 2687d891cc2f1ecd00cace78492cbffc745d9e5b
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 23:06:44 2014 +0100

    First try at implementing delegate

commit c8bada0c7607df936df04b9c7796a03cc2e1a71a
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 19:59:14 2014 +0100

    Don't allow categories to be selected

commit 6b56df5a0e2588d89d757f583070dd2ca78a6e15
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 19:53:36 2014 +0100

    Add outline:0 to TreeView

commit 534ba6e191e711cc7abeea78c3bf470e9eaf0bfd
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 17:57:57 2014 +0100

    More styling

commit 065d40c24d2746f49660485700e3e6327b28810d
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 17:47:40 2014 +0100

    Add more commands, more styling for TreeView

commit 2362d01db40bbc923a51a8bc53ffbabfd18ebea0
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 16:50:31 2014 +0100

    Adjust super()

commit 43ebedff6724af0f217acc14a325febd12c4ed0b
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 16:50:20 2014 +0100

    Style CompletionView

commit 79c1010cf54bd8a3c3f4b2b4a9f97d8ae7d603fb
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 13:06:12 2014 +0100

    Completion refactoring

    Clean up imports
    Whitespace
    Make items non-editable
    Rename nodeToIndex()
    Return QVariant() instead of None
    Remove comments
    Don't inherit TreeItem from object
    Get rid of getValue
    Get rid of child()
    Get rid of childCount
    Remove appendChild
    Get rid of columnCount
    Get rid of parent()
    Renames
    Refactor if
    Simplify columnCount
    Minor fixups

commit a7a6b95f56a87ef03359ec5f9e5d45a906112845
Author: Florian Bruhin <git@the-compiler.org>
Date:   Fri Jan 24 13:04:28 2014 +0100

    Split completion out of command.py

commit cd1c6419ff4034a29a1b48c4ed6ca292944f5674
Author: Florian Bruhin <git@the-compiler.org>
Date:   Thu Jan 23 17:53:37 2014 +0100

    Fix

commit 7073edea92ff8384535f5db80c01168bb8462e75
Author: Florian Bruhin <git@the-compiler.org>
Date:   Thu Jan 23 17:28:03 2014 +0100

    More attempts

commit 7dc522667ba52e9b7f20b3b66e977d23258a3ff4
Author: Florian Bruhin <git@the-compiler.org>
Date:   Thu Jan 23 16:56:01 2014 +0100

    Get rid of parent in setupModelData

commit 5df4874e14818399494f3a47f7feea8b9f86cf69
Author: Florian Bruhin <git@the-compiler.org>
Date:   Thu Jan 23 16:51:52 2014 +0100

    First model/view experiments
This commit is contained in:
Florian Bruhin 2014-01-27 21:35:12 +01:00
parent 7284b21be7
commit f2223064b3
7 changed files with 505 additions and 18 deletions

View File

@ -37,9 +37,10 @@ class QuteBrowser(QApplication):
confdir = self.args.confdir
self.config = Config(confdir)
self.mainwindow = MainWindow()
self.commandparser = cmdutils.CommandParser()
self.keyparser = KeyParser(self.mainwindow)
self.init_cmds()
self.mainwindow = MainWindow()
self.aboutToQuit.connect(self.config.save)
self.mainwindow.tabs.keypress.connect(self.keyparser.handle)
@ -53,7 +54,6 @@ class QuteBrowser(QApplication):
self.keyparser.keystring_updated.connect(
self.mainwindow.status.txt.set_keystring)
self.init_cmds()
self.mainwindow.show()
self.python_hacks()

View File

@ -6,6 +6,8 @@ import shlex
from PyQt5.QtCore import QObject, pyqtSignal
from qutebrowser.utils.completion import CompletionModel
cmd_dict = {}
class ArgumentCountError(TypeError):
@ -81,6 +83,16 @@ class CommandParser(QObject):
raise
self._run(count=count)
class CommandCompletionModel(CompletionModel):
def __init__(self, parent=None):
super().__init__(parent)
assert(cmd_dict)
cmdlist = []
for obj in set(cmd_dict.values()):
cmdlist.append([obj.mainname, obj.desc])
self._data['Commands'] = sorted(cmdlist)
self.init_data()
class Command(QObject):
"""Base skeleton for a command. See the module help for
qutebrowser.commands.commands for details.
@ -92,15 +104,21 @@ class Command(QObject):
nargs = 0
name = None
mainname = None
signal = None
count = False
split_args = True
signal = pyqtSignal(tuple)
desc = "" # FIXME add descriptions everywhere
def __init__(self):
super().__init__()
if self.name is None:
self.name = self.__class__.__name__.lower()
if isinstance(self.name, str):
self.mainname = self.name
else:
self.mainname = self.name[0]
def check(self, args):
"""Check if the argument count is valid. Raise ArgumentCountError if
@ -118,18 +136,14 @@ class Command(QObject):
args -- Arguments to the command.
count -- Command repetition count.
"""
if isinstance(self.name, str):
name = self.name
else:
name = self.name[0]
dbgout = ["command called:", name]
dbgout = ["command called:", self.mainname]
if args:
dbgout += args
if count is not None:
dbgout.append("(count={})".format(count))
logging.debug(' '.join(dbgout))
argv = [name]
argv = [self.mainname]
if args is not None:
argv += args
self.signal.emit((count, argv))

View File

@ -0,0 +1,171 @@
import logging
from collections import OrderedDict
from PyQt5.QtCore import (QAbstractItemModel, Qt, QModelIndex, QVariant,
QSortFilterProxyModel, pyqtSignal)
class CompletionModel(QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data = OrderedDict()
self.parents = []
self.root = CompletionItem([""] * 2)
def removeRows(self, position=0, count=1, parent=QModelIndex()):
node = self.node(parent)
self.beginRemoveRows(parent, position, position + count - 1)
node.childItems.pop(position)
self.endRemoveRows()
def node(self, index):
if index.isValid():
return index.internalPointer()
else:
return self.root
def columnCount(self, parent=QModelIndex()):
return self.root.column_count()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
item = index.internalPointer()
try:
return QVariant(item.data(index.column(), role))
except (IndexError, ValueError):
return QVariant()
def flags(self, index):
# FIXME categories are not selectable, but moving via arrow keys still
# tries to select them
if not index.isValid():
return Qt.NoItemFlags
flags = Qt.ItemIsEnabled
if len(index.internalPointer().children) > 0:
return flags
else:
return flags | Qt.ItemIsSelectable
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.root.data(section))
return None
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
item = index.internalPointer()
try:
item.setdata(index.column(), value, role)
except (IndexError, ValueError):
return False
self.dataChanged.emit(index, index)
return True
def index(self, row, column, parent=QModelIndex()):
if (0 <= row < self.rowCount(parent) and
0 <= column < self.columnCount(parent)):
pass
else:
return QModelIndex()
if not parent.isValid():
parent_item = self.root
else:
parent_item = parent.internalPointer()
child_item = parent_item.children[row]
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex()
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer().parent
if item == self.root or item is None:
return QModelIndex()
return self.createIndex(item.row(), 0, item)
def rowCount(self, parent=QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
pitem = self.root
else:
pitem = parent.internalPointer()
return len(pitem.children)
def init_data(self):
for (cat, items) in self._data.items():
newcat = CompletionItem([cat], self.root)
self.root.children.append(newcat)
for item in items:
newitem = CompletionItem(item, newcat)
newcat.children.append(newitem)
class CompletionItem():
parent = None
_data = None
children = None
_marks = None
def __init__(self, data, parent=None):
self.parent = parent
self._data = data
self.children = []
self._marks = []
def data(self, column, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._data[column]
elif role == Qt.UserRole:
return self._marks
else:
raise ValueError
def setdata(self, column, value, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
self._data[column] = value
elif role == Qt.UserRole:
self._marks = value
else:
raise ValueError
def column_count(self):
return len(self._data)
def row(self):
if self.parent:
return self.parent.children.index(self)
return 0
class CompletionFilterModel(QSortFilterProxyModel):
_pattern = None
# FIXME sort in a way strings starting with pattern are first
@property
def pattern(self):
return self._pattern
@pattern.setter
def pattern(self, val):
self._pattern = val
self.invalidateFilter()
def __init__(self, parent=None):
super().__init__(parent)
self.pattern = ''
def filterAcceptsRow(self, row, parent):
if parent == QModelIndex():
return True
idx = self.sourceModel().index(row, 0, parent);
data = self.sourceModel().data(idx).value()
# TODO more sophisticated filtering
if not self.pattern:
return True
return self.pattern in data

View File

@ -0,0 +1,278 @@
import logging
import html
from PyQt5.QtWidgets import (QTreeView, QStyledItemDelegate, QStyle,
QStyleOptionViewItem)
from PyQt5.QtCore import (QRectF, QRect, QPoint, pyqtSignal, Qt,
QItemSelectionModel)
from PyQt5.QtGui import (QIcon, QPalette, QTextDocument, QTextOption,
QTextCursor)
from qutebrowser.utils.completion import CompletionFilterModel
from qutebrowser.commands.utils import CommandCompletionModel
class CompletionView(QTreeView):
_stylesheet = """
QTreeView {
font-family: Monospace, Courier;
color: #333333;
outline: 0;
}
QTreeView::item {
background: white;
}
QTreeView::item:has-children {
font-weight: bold;
background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #e4e4e4,
stop:1 #dbdbdb);
border-top: 1px solid #808080;
border-bottom: 1px solid #bbbbbb;
}
QTreeView::item:selected {
border-top: 1px solid #f2f2c0;
border-bottom: 1px solid #e6e680;
background-color: #ffec8b;
color: #333333;
}
"""
# FIXME because we use :has-children, if a category is empty, it won't look
# like one anymore
# FIXME somehow only the first column is yellow, even with
# setAllColumnsShowFocus
completion_models = {}
append_cmd_text = pyqtSignal(str)
ignore_next = False
def __init__(self, parent=None):
super().__init__(parent)
self.completion_models[''] = None
self.completion_models['command'] = CommandCompletionModel()
self.model = CompletionFilterModel()
self.setModel(self.model)
self.model.setSourceModel(self.completion_models['command'])
self.setItemDelegate(CompletionItemDelegate())
self.setStyleSheet(self._stylesheet.strip())
self.expandAll()
self.setHeaderHidden(True)
self.setIndentation(0)
self.setItemsExpandable(False)
self.hide()
# FIXME set elidemode
def resizeEvent(self, e):
width = e.size().width()
for i in range(self.model.columnCount()):
self.setColumnWidth(i, width/2)
super().resizeEvent(e)
def setmodel(self, model):
self.model.setSourceModel(self.completion_models[model])
self.model.pattern = ''
self.expandAll()
def resize_to_bar(self, geom):
bottomleft = geom.topLeft()
bottomright = geom.topRight()
delta = QPoint(0, 200)
topleft = bottomleft - delta
self.setGeometry(QRect(topleft, bottomright))
def cmd_text_changed(self, text):
if self.ignore_next:
self.ignore_next = False
return
# FIXME more sophisticated completions
if ' ' in text or not text.startswith(':'):
self.hide()
return
self.setmodel('command')
text = text.lstrip(':')
self.model.pattern = text
self.mark_all_items(text)
self.show()
def first_item(self):
cat = self.model.index(0, 0)
return self.model.index(0, 0, cat)
def last_item(self):
cat = self.model.index(self.model.rowCount() - 1, 0)
return self.model.index(self.model.rowCount(cat) - 1, 0, cat)
def mark_all_items(self, needle):
for i in range(self.model.rowCount()):
cat = self.model.index(i, 0)
for k in range(self.model.rowCount(cat)):
idx = self.model.index(k, 0, cat)
old = self.model.data(idx)
marks = self.get_marks(needle, old)
self.model.setData(idx, marks, Qt.UserRole)
def get_marks(self, needle, haystack):
pos1 = pos2 = 0
marks = []
if not needle:
return marks
while True:
pos1 = haystack.find(needle, pos2)
if pos1 == -1:
break
pos2 = pos1 + len(needle)
marks.append((pos1, pos2))
return marks
def tab_handler(self, shift):
if self.isHidden():
return
selmodel = self.selectionModel()
cur = selmodel.currentIndex()
if not cur.isValid():
idx = self.first_item()
elif shift:
idx = self.indexAbove(cur)
if not idx.isValid():
idx = self.last_item()
cur = idx
else:
idx = self.indexBelow(cur)
if not idx.isValid():
idx = self.first_item()
cur = idx
self.ignore_next = True
selmodel.setCurrentIndex(idx, QItemSelectionModel.ClearAndSelect)
self.append_cmd_text.emit(self.model.data(idx) + ' ')
class CompletionItemDelegate(QStyledItemDelegate):
opt = None
style = None
painter = None
def paint(self, painter, option, index):
painter.save()
self.painter = painter
self.opt = QStyleOptionViewItem(option)
self.initStyleOption(self.opt, index)
self.style = self.opt.widget.style()
self._draw_background()
self._draw_icon()
self._draw_text(index)
self._draw_focus_rect()
painter.restore()
def _draw_background(self):
self.style.drawPrimitive(self.style.PE_PanelItemViewItem, self.opt,
self.painter, self.opt.widget)
def _draw_icon(self):
icon_rect = self.style.subElementRect(
self.style.SE_ItemViewItemDecoration, self.opt, self.opt.widget)
mode = QIcon.Normal
if not self.opt.state & QStyle.State_Enabled:
mode = QIcon.Disabled
elif self.opt.state & QStyle.State_Selected:
mode = QIcon.Selected
state = QIcon.On if self.opt.state & QStyle.State_Open else QIcon.Off
self.opt.icon.paint(self.painter, icon_rect,
self.opt.decorationAlignment, mode, state)
def _draw_text(self, index):
if not self.opt.text:
return
text_rect_ = self.style.subElementRect(self.style.SE_ItemViewItemText,
self.opt, self.opt.widget)
margin = self.style.pixelMetric(QStyle.PM_FocusFrameHMargin, self.opt,
self.opt.widget) + 1
# remove width padding
text_rect = text_rect_.adjusted(margin, 0, -margin, 0)
self.painter.save()
state = self.opt.state
if (state & QStyle.State_Enabled and state & QStyle.State_Active):
cg = QPalette.Normal
elif state & QStyle.State_Enabled:
cg = QPalette.Inactive
else:
cg = QPalette.Disabled
if state & QStyle.State_Selected:
self.painter.setPen(self.opt.palette.color(
cg, QPalette.HighlightedText))
# FIXME this is a dirty fix for the text jumping by one pixel...
# we really should do this properly somehow
text_rect.adjust(0, -1, 0, 0)
else:
self.painter.setPen(self.opt.palette.color(cg, QPalette.Text))
if state & QStyle.State_Editing:
self.painter.setPen(self.opt.palette.color(cg, QPalette.Text))
self.painter.drawRect(text_rect_.adjusted(0, 0, -1, -1))
self.painter.translate(text_rect.left(), text_rect.top())
clip = QRectF(0, 0, text_rect.width(), text_rect.height())
text_option = QTextOption()
if self.opt.features & QStyleOptionViewItem.WrapText:
text_option.setWrapMode(QTextOption.WordWrap)
else:
text_option.setWrapMode(QTextOption.ManualWrap)
text_option.setTextDirection(self.opt.direction)
text_option.setAlignment(QStyle.visualAlignment(
self.opt.direction, self.opt.displayAlignment))
doc = QTextDocument()
if index.parent().isValid():
doc.setPlainText(self.opt.text)
else:
doc.setHtml('<b>{}</b>'.format(html.escape(self.opt.text)))
doc.setDefaultFont(self.opt.font)
doc.setDefaultTextOption(text_option)
doc.setDefaultStyleSheet("""
.highlight {
color: blue;
}
""")
doc.setDocumentMargin(0)
marks = index.data(Qt.UserRole)
for mark in marks:
cur = QTextCursor(doc)
cur.setPosition(mark[0])
cur.setPosition(mark[1], QTextCursor.KeepAnchor)
txt = cur.selectedText()
cur.removeSelectedText()
# FIXME escape html in txt
cur.insertHtml('<span class="highlight">{}</span>'.format(
html.escape(txt)))
doc.drawContents(self.painter, clip)
# FIXME we probably should do eliding here. See
# qcommonstyle.cpp:viewItemDrawText
self.painter.restore()
def _draw_focus_rect(self):
state = self.opt.state
if not state & QStyle.State_HasFocus:
return
o = self.opt
o.rect = self.style.subElementRect(self.style.SE_ItemViewItemFocusRect,
self.opt, self.opt.widget)
o.state |= QStyle.State_KeyboardFocusChange | QStyle.State_Item
if state & QStyle.State_Enabled:
cg = QPalette.Normal
else:
cg = QPalette.Disabled
if state & QStyle.State_Selected:
role = QPalette.Highlight
else:
role = QPalette.Window
o.backgroundColor = self.opt.palette.color(cg, role)
self.style.drawPrimitive(QStyle.PE_FrameFocusRect, o, self.painter,
self.opt.widget)

View File

@ -2,6 +2,7 @@ from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QWidget
from qutebrowser.widgets.statusbar import StatusBar
from qutebrowser.widgets.browser import TabbedBrowser
from qutebrowser.widgets.completion import CompletionView
class MainWindow(QMainWindow):
"""The main window of QuteBrowser"""
@ -25,14 +26,22 @@ class MainWindow(QMainWindow):
self.tabs = TabbedBrowser(self)
self.vbox.addWidget(self.tabs)
self.completion = CompletionView(self)
self.status = StatusBar(self)
self.vbox.addWidget(self.status)
self.status.resized.connect(self.completion.resize_to_bar)
self.tabs.cur_progress.connect(self.status.prog.set_progress)
self.tabs.cur_load_finished.connect(self.status.prog.load_finished)
self.tabs.cur_scroll_perc_changed.connect(self.status.txt.set_perc)
self.status.cmd.esc_pressed.connect(self.tabs.setFocus)
self.status.cmd.hide_completion.connect(self.completion.hide)
self.status.cmd.textChanged.connect(self.completion.cmd_text_changed)
self.status.cmd.tab_pressed.connect(self.completion.tab_handler)
self.completion.append_cmd_text.connect(self.status.cmd.append_cmd)
#self.retranslateUi(MainWindow)
#self.tabWidget.setCurrentIndex(0)
#QtCore.QMetaObject.connectSlotsByName(MainWindow)

View File

@ -1,5 +1,6 @@
import logging
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QHBoxLayout, QWidget
from qutebrowser.widgets.statusbar.command import Command
@ -12,6 +13,7 @@ class StatusBar(QWidget):
cmd = None
txt = None
prog = None
resized = pyqtSignal('QRect')
fgcolor = 'white'
bgcolor = 'black'
font = 'Monospace, Courier'
@ -30,8 +32,8 @@ class StatusBar(QWidget):
self.setStyleSheet(self._stylesheet.strip().format(self=self))
# TODO: the statusbar should be a bit smaller
def __init__(self, parent):
super().__init__(parent)
def __init__(self, mainwindow):
super().__init__(mainwindow)
self.setStyleSheet(self._stylesheet.strip().format(self=self))
self.hbox = QHBoxLayout(self)
self.hbox.setContentsMargins(0, 0, 0, 0)
@ -56,4 +58,6 @@ class StatusBar(QWidget):
self.bgcolor = 'black'
self.txt.error = ''
def resizeEvent(self, e):
super().resizeEvent(e)
self.resized.emit(self.geometry())

View File

@ -1,15 +1,19 @@
import logging
from PyQt5.QtWidgets import QLineEdit, QShortcut, QCompleter
from PyQt5.QtWidgets import QLineEdit, QShortcut
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QValidator, QKeySequence
from qutebrowser.commands.utils import CommandCompletionModel
class Command(QLineEdit):
"""The commandline part of the statusbar"""
got_cmd = pyqtSignal(str) # Emitted when a command is triggered by the user
bar = None # The status bar object
esc_pressed = pyqtSignal() # Emitted when escape is pressed
tab_pressed = pyqtSignal(bool) # Emitted when tab is pressed (arg: shift)
hide_completion = pyqtSignal() # Hide completion window
history = [] # The command history, with newer commands at the bottom
_tmphist = []
_histpos = None
@ -20,16 +24,17 @@ class Command(QLineEdit):
def __init__(self, bar):
super().__init__(bar)
# FIXME
self.bar = bar
self.setStyleSheet("border: 0px; padding-left: 1px")
self.setValidator(Validator())
self.setCompleter(Completer())
self.returnPressed.connect(self.process_cmd)
self.textEdited.connect(self._histbrowse_stop)
for (key, handler) in [(Qt.Key_Escape, self.esc_pressed),
(Qt.Key_Up, self.key_up_handler),
(Qt.Key_Down, self.key_down_handler),
(Qt.Key_Tab | Qt.SHIFT, self.key_stab_handler),
(Qt.Key_Tab, self.key_tab_handler)]:
sc = QShortcut(self)
sc.setKey(QKeySequence(key))
@ -50,12 +55,19 @@ class Command(QLineEdit):
self.setText(':' + text)
self.setFocus()
def append_cmd(self, text):
"""Append text to the commandline"""
# FIXME do the right thing here
self.setText(':' + text)
self.setFocus()
def focusOutEvent(self, e):
"""Clear the statusbar text if it's explicitely unfocused"""
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
self._histbrowse_stop()
self.hide_completion.emit()
super().focusOutEvent(e)
def focusInEvent(self, e):
@ -102,8 +114,10 @@ class Command(QLineEdit):
self.set_cmd(self._tmphist[self._histpos])
def key_tab_handler(self):
# TODO implement tab completion
logging.debug('tab pressed')
self.tab_pressed.emit(False)
def key_stab_handler(self):
self.tab_pressed.emit(True)
class Validator(QValidator):
"""Validator to prevent the : from getting deleted"""
@ -113,6 +127,3 @@ class Validator(QValidator):
else:
return (QValidator.Invalid, string, pos)
class Completer(QCompleter):
def __init__(self):
super().__init__([':foo', ':bar', 'baz'])