mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement scrolling
This commit is contained in:
parent
8b35b6aa34
commit
acb1742432
@ -44,9 +44,11 @@ def get_theme():
|
|||||||
class TextBrowser(PlainTextEdit): # {{{
|
class TextBrowser(PlainTextEdit): # {{{
|
||||||
|
|
||||||
resized = pyqtSignal()
|
resized = pyqtSignal()
|
||||||
|
wheel_event = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, right=False, parent=None):
|
def __init__(self, right=False, parent=None):
|
||||||
PlainTextEdit.__init__(self, parent)
|
PlainTextEdit.__init__(self, parent)
|
||||||
|
self.setFocusPolicy(Qt.NoFocus)
|
||||||
self.right = right
|
self.right = right
|
||||||
self.setReadOnly(True)
|
self.setReadOnly(True)
|
||||||
w = self.fontMetrics()
|
w = self.fontMetrics()
|
||||||
@ -216,6 +218,12 @@ class TextBrowser(PlainTextEdit): # {{{
|
|||||||
painter.drawLine(0, top, w, top)
|
painter.drawLine(0, top, w, top)
|
||||||
painter.drawLine(0, bottom - 1, w, bottom - 1)
|
painter.drawLine(0, bottom - 1, w, bottom - 1)
|
||||||
|
|
||||||
|
def wheelEvent(self, ev):
|
||||||
|
if ev.orientation() == Qt.Vertical:
|
||||||
|
self.wheel_event.emit(ev)
|
||||||
|
else:
|
||||||
|
return PlainTextEdit.wheelEvent(self, ev)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class Highlight(QTextDocument): # {{{
|
class Highlight(QTextDocument): # {{{
|
||||||
@ -253,6 +261,7 @@ class Highlight(QTextDocument): # {{{
|
|||||||
class DiffSplitHandle(QSplitterHandle): # {{{
|
class DiffSplitHandle(QSplitterHandle): # {{{
|
||||||
|
|
||||||
WIDTH = 30 # px
|
WIDTH = 30 # px
|
||||||
|
wheel_event = pyqtSignal(object)
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event):
|
||||||
QSplitterHandle.paintEvent(self, event)
|
QSplitterHandle.paintEvent(self, event)
|
||||||
@ -339,6 +348,12 @@ class DiffSplitHandle(QSplitterHandle): # {{{
|
|||||||
ans = QSplitterHandle.sizeHint(self)
|
ans = QSplitterHandle.sizeHint(self)
|
||||||
ans.setWidth(self.WIDTH)
|
ans.setWidth(self.WIDTH)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def wheelEvent(self, ev):
|
||||||
|
if ev.orientation() == Qt.Vertical:
|
||||||
|
self.wheel_event.emit(ev)
|
||||||
|
else:
|
||||||
|
return QSplitterHandle.wheelEvent(self, ev)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class DiffSplit(QSplitter): # {{{
|
class DiffSplit(QSplitter): # {{{
|
||||||
@ -583,8 +598,12 @@ class DiffSplit(QSplitter): # {{{
|
|||||||
|
|
||||||
class DiffView(QWidget):
|
class DiffView(QWidget):
|
||||||
|
|
||||||
|
SYNC_POSITION = 0.4
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
|
self.changes = []
|
||||||
|
self.delta = 0
|
||||||
self.l = l = QHBoxLayout(self)
|
self.l = l = QHBoxLayout(self)
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
l.setMargin(0), l.setSpacing(0)
|
l.setMargin(0), l.setSpacing(0)
|
||||||
@ -598,10 +617,57 @@ class DiffView(QWidget):
|
|||||||
self.bars.append(bar)
|
self.bars.append(bar)
|
||||||
bar.valueChanged[int].connect(partial(self.scrolled, i))
|
bar.valueChanged[int].connect(partial(self.scrolled, i))
|
||||||
self.view.left.resized.connect(self.adjust_range)
|
self.view.left.resized.connect(self.adjust_range)
|
||||||
|
for v in self.view.left, self.view.right, self.view.handle(1):
|
||||||
|
v.wheel_event.connect(self.scrollbar.wheelEvent)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def syncpos(self):
|
||||||
|
return self.scrollbar.value() + int(self.scrollbar.pageStep() * self.SYNC_POSITION)
|
||||||
|
|
||||||
|
def get_position_from_scrollbar(self, which):
|
||||||
|
changes = (self.changes, self.view.left.changes, self.view.right.changes)[which]
|
||||||
|
bar = self.bars[which]
|
||||||
|
syncpos = self.syncpos + bar.value()
|
||||||
|
prev = (0, 0, None)
|
||||||
|
for i, (top, bot, kind) in enumerate(changes):
|
||||||
|
if syncpos <= bot:
|
||||||
|
if top <= syncpos and top != bot:
|
||||||
|
# syncpos is inside a change
|
||||||
|
ratio = float(syncpos - top) / (bot - top)
|
||||||
|
return 'in', i, ratio
|
||||||
|
else:
|
||||||
|
# syncpos is after the change
|
||||||
|
offset = syncpos - prev[1]
|
||||||
|
return 'after', i - 1, offset
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
prev = (top, bot, kind)
|
||||||
|
else:
|
||||||
|
offset = syncpos - prev[1]
|
||||||
|
return 'after', len(self.changes) - 1, offset
|
||||||
|
|
||||||
|
def scroll_to(self, which, position):
|
||||||
|
changes = (self.changes, self.view.left.changes, self.view.right.changes)[which]
|
||||||
|
bar = self.bars[which]
|
||||||
|
syncpos = self.syncpos
|
||||||
|
val = None
|
||||||
|
if position[0] == 'in':
|
||||||
|
change_idx, ratio = position[1:]
|
||||||
|
start, end = changes[change_idx][:2]
|
||||||
|
val = start + int((end - start) * ratio)
|
||||||
|
else:
|
||||||
|
change_idx, offset = position[1:]
|
||||||
|
start = 0 if change_idx < 0 else changes[change_idx][1]
|
||||||
|
val = start + offset
|
||||||
|
bar.setValue(val - syncpos)
|
||||||
|
|
||||||
def scrolled(self, which):
|
def scrolled(self, which):
|
||||||
if self.syncing:
|
if self.syncing:
|
||||||
return
|
return
|
||||||
|
position = self.get_position_from_scrollbar(which)
|
||||||
|
with self:
|
||||||
|
for x in {0, 1, 2} - {which}:
|
||||||
|
self.scroll_to(x, position)
|
||||||
self.view.handle(1).update()
|
self.view.handle(1).update()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
@ -613,27 +679,51 @@ class DiffView(QWidget):
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
self.view.clear()
|
self.view.clear()
|
||||||
self.changes = []
|
self.changes = []
|
||||||
|
self.delta = 0
|
||||||
|
|
||||||
def adjust_range(self):
|
def adjust_range(self):
|
||||||
ls, rs = self.view.left.verticalScrollBar(), self.view.right.verticalScrollBar()
|
ls, rs = self.view.left.verticalScrollBar(), self.view.right.verticalScrollBar()
|
||||||
page_step = self.view.left.verticalScrollBar().pageStep()
|
page_step = self.view.left.verticalScrollBar().pageStep()
|
||||||
self.scrollbar.setPageStep(min(ls.pageStep(), rs.pageStep()))
|
self.scrollbar.setPageStep(min(ls.pageStep(), rs.pageStep()))
|
||||||
self.scrollbar.setSingleStep(min(ls.singleStep(), rs.singleStep()))
|
self.scrollbar.setSingleStep(min(ls.singleStep(), rs.singleStep()))
|
||||||
self.scrollbar.setRange(0, max(ls.maximum(), rs.maximum()))
|
self.scrollbar.setRange(0, ls.maximum() + self.delta)
|
||||||
self.scrollbar.setVisible(self.scrollbar.maximum() > page_step)
|
self.scrollbar.setVisible(self.scrollbar.maximum() > page_step)
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
self.view.finalize()
|
self.view.finalize()
|
||||||
self.changes = []
|
self.changes = []
|
||||||
|
self.calculate_length()
|
||||||
|
|
||||||
|
def calculate_length(self):
|
||||||
left, right = self.view.left, self.view.right
|
left, right = self.view.left, self.view.right
|
||||||
ldoc, rdoc = left.document(), right.document()
|
changes = []
|
||||||
|
delta = 0
|
||||||
for (l_top, l_bot, kind), (r_top, r_bot, kind) in zip(left.changes, right.changes):
|
for (l_top, l_bot, kind), (r_top, r_bot, kind) in zip(left.changes, right.changes):
|
||||||
pass
|
height = max(l_bot - l_top, r_bot - r_top)
|
||||||
|
top = delta + l_top
|
||||||
|
changes.append((top, top + height, kind))
|
||||||
|
delta = top + height - l_bot
|
||||||
|
self.changes, self.delta = changes, delta
|
||||||
self.adjust_range()
|
self.adjust_range()
|
||||||
|
|
||||||
|
def keyPressEvent(self, ev):
|
||||||
|
amount, d = None, 1
|
||||||
|
key = ev.key()
|
||||||
|
if key in (Qt.Key_Up, Qt.Key_Down, Qt.Key_J, Qt.Key_K):
|
||||||
|
amount = self.scrollbar.singleStep()
|
||||||
|
if key in (Qt.Key_Up, Qt.Key_K):
|
||||||
|
d = -1
|
||||||
|
elif key in (Qt.Key_PageUp, Qt.Key_PageDown):
|
||||||
|
amount = self.scrollbar.pageStep()
|
||||||
|
if key in (Qt.Key_PageUp,):
|
||||||
|
d = -1
|
||||||
|
elif key in (Qt.Key_Home, Qt.Key_End):
|
||||||
|
self.scrollbar.setValue(0 if key == Qt.Key_Home else self.scrollbar.maximum())
|
||||||
|
|
||||||
|
if amount is not None:
|
||||||
|
self.scrollbar.setValue(self.scrollbar.value() + d * amount)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
w = DiffView()
|
w = DiffView()
|
||||||
@ -646,4 +736,3 @@ if __name__ == '__main__':
|
|||||||
app.exec_()
|
app.exec_()
|
||||||
|
|
||||||
# TODO: Add diff colors for other color schemes
|
# TODO: Add diff colors for other color schemes
|
||||||
# TODO: Handle scroll wheel and key up/down events
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user