replace ScaledScrollBar with BigScrollBar

step 4 - add repeat actions

This has probably a problem. The repeat action is triggering updates asynchronously.
Which means we do not wait until it is done. Which means we can DOS ourselves.
This commit is contained in:
2024-04-14 19:12:37 +02:00
parent 7a574f7ed4
commit 3e793596c2

View File

@@ -2,7 +2,7 @@ import enum
from PySide6.QtGui import QWheelEvent from PySide6.QtGui import QWheelEvent
from PySide6.QtWidgets import QWidget, QStylePainter, QStyle, QStyleOptionSlider, QSlider, QAbstractSlider from PySide6.QtWidgets import QWidget, QStylePainter, QStyle, QStyleOptionSlider, QSlider, QAbstractSlider
from PySide6.QtCore import Qt, QSize, QEvent, QRect, QPoint, Signal from PySide6.QtCore import Qt, QSize, QEvent, QRect, QPoint, Signal, QTimer, QElapsedTimer
class BigScrollBar(QWidget): class BigScrollBar(QWidget):
@@ -33,6 +33,11 @@ class BigScrollBar(QWidget):
def __init__(self): def __init__(self):
super(BigScrollBar, self).__init__() super(BigScrollBar, self).__init__()
self.repeat_action_timer = QTimer()
self.repeat_action_control = QStyle.SubControl.SC_None
self.repeat_action_timer.setSingleShot(True)
self.repeat_action_timer.timeout.connect(self.execute_control)
self.repeat_action_timer.setInterval(50)
def setMinimum(self, min: int): def setMinimum(self, min: int):
self.minimum = min self.minimum = min
@@ -110,8 +115,14 @@ class BigScrollBar(QWidget):
return super().event(event) return super().event(event)
def mousePressEvent(self, event): def mousePressEvent(self, event):
if self.repeat_action_timer.isActive():
self.stopRepeatAction()
if not (event.button() == Qt.MouseButton.LeftButton or event.button() == Qt.MouseButton.MiddleButton):
return
style_options = self.style_options() style_options = self.style_options()
style_options.keyboardModifiers = event.modifiers() ## not sure why, copied from https://github.com/qt/qtbase/blob/dev/src/widgets/widgets/qscrollbar.cpp#L535
self.pressedControl = self.style().hitTestComplexControl(QStyle.ComplexControl.CC_ScrollBar, style_options, self.pressedControl = self.style().hitTestComplexControl(QStyle.ComplexControl.CC_ScrollBar, style_options,
event.position().toPoint(), self) event.position().toPoint(), self)
@@ -120,14 +131,11 @@ class BigScrollBar(QWidget):
sr: QRect = self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, style_options, sr: QRect = self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, style_options,
QStyle.SubControl.SC_ScrollBarSlider, self) QStyle.SubControl.SC_ScrollBarSlider, self)
click: QPoint = event.position().toPoint() click: QPoint = event.position().toPoint()
press_y_value = click.y() - sr.center().y() + sr.topLeft().y()
press_y_value = self.pixelPosToRangeValue(press_y_value)
# print(f"pressYValue {pressYValue}") # print(f"pressYValue {pressYValue}")
if self.pressedControl == QStyle.SubControl.SC_ScrollBarSlider: if self.pressedControl == QStyle.SubControl.SC_ScrollBarSlider:
self.click_offset = click.y() - sr.y() self.click_offset = click.y() - sr.y()
# todo
if (self.pressedControl == QStyle.SubControl.SC_ScrollBarAddPage if (self.pressedControl == QStyle.SubControl.SC_ScrollBarAddPage
or self.pressedControl == QStyle.SubControl.SC_ScrollBarSubPage) \ or self.pressedControl == QStyle.SubControl.SC_ScrollBarSubPage) \
and event.button() == Qt.MouseButton.MiddleButton: and event.button() == Qt.MouseButton.MiddleButton:
@@ -135,30 +143,44 @@ class BigScrollBar(QWidget):
self.set_value(self.pixelPosToRangeValue(event.position().toPoint().y())) self.set_value(self.pixelPosToRangeValue(event.position().toPoint().y()))
self.pressedControl = QStyle.SubControl.SC_ScrollBarSlider self.pressedControl = QStyle.SubControl.SC_ScrollBarSlider
self.click_offset = slider_length / 2 self.click_offset = slider_length / 2
pass return
self.activateControl(self.pressedControl) self.repeat_action_control = self.pressedControl
self.execute_control()
# self.repaint(self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, style_options, self.pressedControl)) # self.repaint(self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, style_options, self.pressedControl))
def activateControl(self, control): def execute_control(self):
match control: # print(f"execute_control: {self.repeat_action_control}")
trigger_repeat_action = True
match self.repeat_action_control:
case QStyle.SubControl.SC_ScrollBarAddPage: case QStyle.SubControl.SC_ScrollBarAddPage:
self.scroll_event.emit(self.ScrollEvent.PageDown) self.scroll_event.emit(self.ScrollEvent.PageDown)
case QStyle.SubControl.SC_ScrollBarSubPage: case QStyle.SubControl.SC_ScrollBarSubPage:
self.scroll_event.emit(self.ScrollEvent.PageUp) self.scroll_event.emit(self.ScrollEvent.PageUp)
if self.value <= self.minimum:
trigger_repeat_action = False
case QStyle.SubControl.SC_ScrollBarAddLine: case QStyle.SubControl.SC_ScrollBarAddLine:
self.scroll_event.emit(self.ScrollEvent.LinesDown) self.scroll_event.emit(self.ScrollEvent.LinesDown)
case QStyle.SubControl.SC_ScrollBarSubLine: case QStyle.SubControl.SC_ScrollBarSubLine:
self.scroll_event.emit(self.ScrollEvent.LinesUp) self.scroll_event.emit(self.ScrollEvent.LinesUp)
if self.value <= self.minimum:
trigger_repeat_action = False
case QStyle.SubControl.SC_ScrollBarFirst: case QStyle.SubControl.SC_ScrollBarFirst:
self.set_value(self.minimum) self.set_value(self.minimum)
trigger_repeat_action = False
case QStyle.SubControl.SC_ScrollBarLast: case QStyle.SubControl.SC_ScrollBarLast:
self.set_value(self.maximum) self.set_value(self.maximum)
trigger_repeat_action = False
case _: case _:
pass trigger_repeat_action = False
if trigger_repeat_action:
# print(f"schedule repeat action: {self.repeat_action_control}")
self.repeat_action_timer.start()
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
self.pressedControl = QStyle.SubControl.SC_None self.pressedControl = QStyle.SubControl.SC_None
self.stopRepeatAction()
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
if self.pressedControl == QStyle.SubControl.SC_None: if self.pressedControl == QStyle.SubControl.SC_None:
@@ -177,6 +199,12 @@ class BigScrollBar(QWidget):
# print(f"move to value: {new_position}") # print(f"move to value: {new_position}")
self.set_value(new_position) self.set_value(new_position)
# stop repeat action when pointer leaves control
pr: QRect = self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, self.style_options(),
self.pressedControl, self)
if not pr.contains(event.position().toPoint()):
self.stopRepeatAction()
def wheelEvent(self, event: QWheelEvent): def wheelEvent(self, event: QWheelEvent):
event.ignore() event.ignore()
@@ -188,7 +216,6 @@ class BigScrollBar(QWidget):
scroll_event = self.ScrollEvent.LinesDown if event.angleDelta().y() < 0 else self.ScrollEvent.LinesUp scroll_event = self.ScrollEvent.LinesDown if event.angleDelta().y() < 0 else self.ScrollEvent.LinesUp
self.scroll_event.emit(scroll_event) self.scroll_event.emit(scroll_event)
def pixelPosToRangeValue(self, pos: int): def pixelPosToRangeValue(self, pos: int):
opt: QStyleOptionSlider = self.style_options() opt: QStyleOptionSlider = self.style_options()
@@ -198,18 +225,24 @@ class BigScrollBar(QWidget):
QStyle.SubControl.SC_ScrollBarSlider, self) QStyle.SubControl.SC_ScrollBarSlider, self)
# only for vertical scrollbars # only for vertical scrollbars
sliderLength = sr.height(); slider_length = sr.height();
sliderMin = gr.y(); slider_min = gr.y();
sliderMax = gr.bottom() - sliderLength + 1; slider_max = gr.bottom() - slider_length + 1;
val = QStyle.sliderValueFromPosition(opt.minimum, opt.maximum, pos - sliderMin, val = QStyle.sliderValueFromPosition(opt.minimum, opt.maximum, pos - slider_min,
sliderMax - sliderMin, opt.upsideDown) slider_max - slider_min, opt.upsideDown)
t = self.scale / self.maximum t = self.scale / self.maximum
val = int(val / t) val = int(val / t)
# print(f"pixelPosToRangeValue({pos}) -> {val}") # print(f"pixelPosToRangeValue({pos}) -> {val}")
return val return val
def stopRepeatAction(self):
self.repeat_action_control = QStyle.SubControl.SC_None
# print(f"stop repeat action: {self.repeat_action_control}")
self.repeat_action_timer.stop()
#self.update()
def set_value(self, value: int): def set_value(self, value: int):
self.value = value self.value = value
self.value_changed.emit(str(self.value)) self.value_changed.emit(str(self.value))