137 lines
4.2 KiB
Python
137 lines
4.2 KiB
Python
from kivy.lang import Builder
|
|
from kivy.properties import BooleanProperty, ListProperty, NumericProperty
|
|
from kivy.uix.widget import Widget
|
|
from kivymd.uix.label import MDLabel
|
|
from kivymd.uix.selectioncontrol import Thumb
|
|
|
|
|
|
class SelectionSlider(Widget):
|
|
__events__ = ["on_select", "on_change"]
|
|
active = BooleanProperty(False)
|
|
hint = BooleanProperty(True)
|
|
|
|
index = NumericProperty(0) # selected index
|
|
values = ListProperty([(0, "")]) # (value:numeric,label:string) pairs
|
|
normalized_pos = NumericProperty(0) # slider relative pos from 0-1
|
|
px_pos = NumericProperty(0) # actual px pos
|
|
padding = NumericProperty("16sp")
|
|
|
|
track_color = ListProperty([1, 1, 1, 0.3])
|
|
thumb_color = ListProperty([0.5, 0.5, 0.5, 1])
|
|
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.bind(
|
|
size=self.set_index_and_positions,
|
|
pos=self.set_index_and_positions,
|
|
values=self.set_index_and_positions,
|
|
index=self.set_index_and_positions,
|
|
)
|
|
|
|
def set_index_and_positions(self, *_args):
|
|
self.index = max(0, min(self.index, len(self.values) - 1))
|
|
self.normalized_pos = self.index / (len(self.values) - 1)
|
|
self.px_pos = self.x + self.padding + self.normalized_pos * (self.width - 2 * self.padding)
|
|
|
|
@property
|
|
def value(self):
|
|
return self.values[self.index][0]
|
|
|
|
def set_value(self, set_value): # set to closest value
|
|
eq_value = sorted([(abs(v - set_value), i) for i, (v, l) in enumerate(self.values)])
|
|
self.index = eq_value[0][1]
|
|
|
|
def set_from_pos(self, pos):
|
|
norm_value = (pos[0] - self.x - self.padding) / (self.width - 2 * self.padding)
|
|
self.index = round(norm_value * (len(self.values) - 1))
|
|
self.dispatch("on_change", self.value)
|
|
|
|
def on_touch_down(self, touch):
|
|
if self.disabled or not self.collide_point(*touch.pos):
|
|
return
|
|
if touch.is_mouse_scrolling:
|
|
if "down" in touch.button or "left" in touch.button:
|
|
self.index += 1
|
|
if "up" in touch.button or "right" in touch.button:
|
|
self.index -= 1
|
|
else:
|
|
touch.grab(self)
|
|
self.active = True
|
|
self.set_from_pos(touch.pos)
|
|
return True
|
|
|
|
def on_touch_move(self, touch):
|
|
if touch.grab_current == self:
|
|
self.set_from_pos(touch.pos)
|
|
return True
|
|
|
|
def on_touch_up(self, touch):
|
|
if touch.grab_current == self:
|
|
self.set_from_pos(touch.pos)
|
|
self.active = False
|
|
self.dispatch("on_select", self.value)
|
|
return True
|
|
|
|
def on_select(self, value):
|
|
pass
|
|
|
|
def on_change(self, value):
|
|
pass
|
|
|
|
|
|
KV = """
|
|
#:import i18n katrain.core.lang.i18n
|
|
<SelectionSlider>:
|
|
id: slider
|
|
canvas:
|
|
Clear
|
|
Color:
|
|
rgba:
|
|
self.track_color
|
|
Rectangle:
|
|
size:
|
|
(self.width - self.padding*2, dp(4))
|
|
pos:
|
|
(self.x + self.padding, self.center_y - dp(4))
|
|
Color:
|
|
rgba:
|
|
self.thumb_color
|
|
Rectangle:
|
|
size:
|
|
((self.width-self.padding*2)*self.normalized_pos, sp(4))
|
|
pos:
|
|
(self.x + self.padding, self.center_y - dp(4))
|
|
|
|
Thumb:
|
|
id: thumb
|
|
size_hint: None, None
|
|
size:
|
|
((dp(24), dp(24)) if root.active else (dp(16), dp(16)))
|
|
pos:
|
|
(slider.px_pos - dp(8), slider.center_y - thumb.height/2 - dp(2))
|
|
color:
|
|
root.thumb_color
|
|
elevation:
|
|
4 if root.active else 2
|
|
|
|
MDCard:
|
|
id: hint_box
|
|
size_hint: None, None
|
|
md_bg_color: [1, 1, 1, 1] if root.active else [0, 0, 0, 0]
|
|
elevation: 4 if root.active else 0
|
|
size:
|
|
(max(dp(28), label.texture_size[0]+4) , dp(28))
|
|
pos:
|
|
(slider.px_pos - dp(9), slider.center_y - hint_box.height / 2 + dp(30))
|
|
|
|
Label:
|
|
id: label
|
|
text: slider.values[slider.index][1]
|
|
font_size: dp(12)
|
|
lang_change_tracking: i18n._('') # for font
|
|
halign: "center"
|
|
color: root.thumb_color if root.active else [0, 0, 0, 0]
|
|
"""
|
|
|
|
Builder.load_string(KV)
|