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:
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user