Skip to content

Instantly share code, notes, and snippets.

@tshirtman
Created April 17, 2021 10:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tshirtman/26bd680db5e847bab83387efaafd78f2 to your computer and use it in GitHub Desktop.
Save tshirtman/26bd680db5e847bab83387efaafd78f2 to your computer and use it in GitHub Desktop.
using a shader to build a bottom dock for buttons with a circle opening in it
'''
'''
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.graphics import RenderContext
from kivy import properties as P
KV = '''
#:import rgba kivy.utils.rgba
<CLabel@Label,CSlider@Slider>:
size_hint_y: None
height: '50dp'
<CColorPicker@ColorPicker>
size_hint_y: None
height: '400dp'
FloatLayout:
ScrollView:
GridLayout:
size_hint_y: None
height: self.minimum_height
cols: 2
CLabel:
text: 'height'
CSlider:
id: height
min: 0
max: 1000
CLabel:
text: 'radius'
CSlider:
id: radius
min: 0
max: 1000
CLabel:
text: 'corner_radius'
CSlider:
id: corner_radius
min: 0
max: 100
CLabel:
text: 'ease'
CSlider:
id: ease
min: 0
max: 10
CLabel:
text: 'color'
CColorPicker:
id: color
color: rgba('#009688')
Dock:
radius: radius.value
corner_radius: corner_radius.value
color: color.color or (0, 0, 0, 0)
size_hint: 1, None
height: height.value
ease: ease.value
<Dock>:
canvas:
Rectangle:
pos: self.pos
size: self.size
'''
DOCK_SHADER = '''
$HEADER$
uniform vec2 resolution;
uniform float radius;
uniform float corner_radius;
uniform float ease;
uniform vec4 color;
// not available in glsl2, let's define it
float smooth_step(float edge0, float edge1, float x){
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
void main(void) {
vec4 frag_coord = frag_modelview_mat * gl_FragCoord;
float x = frag_coord.x;
float y = frag_coord.y;
vec2 circle_center = vec2(resolution.x / 2.0, resolution.y);
// smooth based on distance from circle center
float d = length(frag_coord.xy - circle_center);
float opacity = smooth_step(radius, radius + ease, d);
// smooth based on distance to top
opacity *= 1.0 - smooth_step(-ease, 0.0, frag_coord.y - resolution.y);
// round the corners
// left
vec2 left_corner = vec2(
circle_center.x - radius - corner_radius,
resolution.y - corner_radius
);
if (
x > left_corner.x && x < circle_center.x - radius
&& y > left_corner.y && y < resolution.y
) {
opacity *= 1.0 - smooth_step(
corner_radius - ease,
corner_radius,
length(frag_coord.xy - left_corner)
);
}
// right
vec2 right_corner = vec2(
circle_center.x + radius + corner_radius,
resolution.y - corner_radius
);
if (
x < right_corner.x && x > circle_center.x + radius
&& y > right_corner.y && y < resolution.y
) {
opacity *= 1.0 - smooth_step(
corner_radius - ease,
corner_radius,
length(frag_coord.xy - right_corner)
);
}
gl_FragColor = vec4(color.r, color.g, color.b, color.a * opacity);
}
'''
class Dock(Widget):
radius = P.NumericProperty('50dp')
color = P.ListProperty([1, 1, 1, 1])
ease = P.NumericProperty(2)
corner_radius = P.NumericProperty('10dp')
def __init__(self, **kwargs):
self.canvas = RenderContext()
self.canvas.shader.fs = DOCK_SHADER
super().__init__(**kwargs)
self.bind(
size=self.update_glsl,
pos=self.update_glsl,
radius=self.update_glsl,
corner_radius=self.update_glsl,
color=self.update_glsl,
ease=self.update_glsl,
)
self.update_glsl()
def update_glsl(self, *largs):
self.canvas['resolution'] = list(map(float, self.size))
self.canvas['radius'] = float(self.radius)
self.canvas['corner_radius'] = float(self.corner_radius)
self.canvas['color'] = list(map(float, self.color))
self.canvas['ease'] = float(self.ease)
win_rc = Window.render_context
self.canvas['projection_mat'] = win_rc['projection_mat']
self.canvas['modelview_mat'] = win_rc['modelview_mat']
self.canvas['frag_modelview_mat'] = win_rc['frag_modelview_mat']
class Application(App):
def build(self):
return Builder.load_string(KV)
if __name__ == "__main__":
Application().run()
@tshirtman
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment