diff --git a/data/io.github.fsobolev.Cavalier.gschema.xml b/data/io.github.fsobolev.Cavalier.gschema.xml
index 8943234..78985fe 100644
--- a/data/io.github.fsobolev.Cavalier.gschema.xml
+++ b/data/io.github.fsobolev.Cavalier.gschema.xml
@@ -25,7 +25,6 @@
Defines what the visualizer looks like.
-
@@ -47,16 +46,21 @@
Roundness of items
- This setting only affects "levels" and "particles" modes.
+ This setting affects "levels", "particles" and "spine" modes.
10
Thickness of lines
- Thickness of lines in "line" mode (in pixels).
+ Thickness of lines when filling is off (in pixels).
15
+
+ Filling
+ Whether shapes should be filled or outlined.
+ true
+
Number of bars
Number of bars in CAVA config
diff --git a/src/draw_functions.py b/src/draw_functions.py
index 5b964ae..bf6ea3d 100644
--- a/src/draw_functions.py
+++ b/src/draw_functions.py
@@ -58,7 +58,7 @@ def draw_element(cr, x, y, width, height, radius):
radius * min(width, height) / 100, 90 * degrees, -180 * degrees)
cr.close_path()
-def wave(sample, cr, width, height, colors):
+def wave(sample, cr, width, height, colors, fill, thickness):
set_source(cr, height, colors)
ls = len(sample)
cr.move_to(0, (1.0 - sample[0]) * height)
@@ -67,76 +67,99 @@ def wave(sample, cr, width, height, colors):
cr.rel_curve_to(width / (ls - 1) * 0.5, 0.0, \
width / (ls - 1) * 0.5, height_diff * height, \
width / (ls - 1), height_diff * height)
- cr.line_to(width, height)
- cr.line_to(0, height)
- cr.fill()
-
-def line(sample, cr, width, height, colors, thickness):
- set_source(cr, height, colors)
- ls = len(sample)
- cr.move_to(0, (1.0 - sample[0]) * height)
- cr.set_line_width(thickness)
- for i in range(ls - 1):
- height_diff = (sample[i] - sample[i+1])
- cr.rel_curve_to(width / (ls - 1) * 0.5, 0.0, \
- width / (ls - 1) * 0.5, height_diff * height, \
- width / (ls - 1), height_diff * height)
- cr.stroke()
+ if fill:
+ cr.line_to(width, height)
+ cr.line_to(0, height)
+ cr.fill()
+ else:
+ cr.set_line_width(thickness)
+ cr.stroke()
-def levels(sample, cr, width, height, colors, offset, radius):
+def levels(sample, cr, width, height, colors, offset, radius, fill, thickness):
set_source(cr, height, colors)
ls = len(sample)
step = width / ls
+ offset_px = step * offset / 100
+ if fill:
+ thickness = 0
+ else:
+ thickness = min(thickness, \
+ (step - offset_px * 2) / 2,
+ (height / 10 - offset_px) / 2)
+ cr.set_line_width(thickness)
for i in range(ls):
q = int(round(sample[i], 1) * 10)
for r in range(q):
- draw_element(cr, step * i + step * offset / 100, \
- height - (height / 10 * (r + 1)) * (1 - offset / 400), \
- max(step - step * offset / 100 * 2, 1), \
- max(height / 10 * (1 - offset / 50), 1), radius)
- cr.fill()
+ draw_element(cr, step * i + offset_px + thickness / 2, \
+ height - (height / 10 * (r + 1)) + offset_px / 2 + thickness / 2, \
+ max(step - offset_px * 2 - thickness, 1), \
+ max(height / 10 - offset_px - thickness, 1), radius)
+ cr.fill() if fill else cr.stroke()
-def particles(sample, cr, width, height, colors, offset, radius):
+def particles(sample, cr, width, height, colors, offset, radius, fill, thickness):
set_source(cr, height, colors)
ls = len(sample)
step = width / ls
+ offset_px = step * offset / 100
+ if fill:
+ thickness = 0
+ else:
+ thickness = min(thickness, \
+ (step - offset_px * 2) / 2,
+ (height / 10) / 2)
+ cr.set_line_width(thickness)
for i in range(ls):
- draw_element(cr, step * i + step * offset / 100, \
- height * 0.9 - height * 0.9 * sample[i], \
- max(step - step * offset / 100 * 2, 1), \
- max(height / 10, 1), radius)
- cr.fill()
+ draw_element(cr, step * i + offset_px + thickness / 2, \
+ height * 0.9 - height * 0.9 * sample[i] + thickness / 2, \
+ max(step - offset_px * 2 - thickness, 1), \
+ max(height / 10 - thickness, 1), radius)
+ cr.fill() if fill else cr.stroke()
-def spine(sample, cr, width, height, colors, offset, radius):
+def spine(sample, cr, width, height, colors, offset, radius, fill, thickness):
ls = len(sample)
+ cr.set_line_width(thickness)
if height > width:
step = height / ls
for i in range(ls):
set_source(cr, height, colors, sample[i] - (0.95 - i / ls))
offset_px = step * offset / 100 * sample[i]
- draw_element(cr, width / 2 - sample[i] * step / 2 + offset_px, \
- step * i + step / 2 - sample[i] * step / 2 + offset_px, \
- step * sample[i] - offset_px * 2, \
- step * sample[i] - offset_px * 2, radius)
- cr.fill()
+ if fill:
+ thickness = 0
+ else:
+ thickness = min(thickness, \
+ (step * sample[i] - offset_px * 2) / 2)
+ cr.set_line_width(thickness)
+ draw_element(cr, width / 2 - sample[i] * step / 2 + offset_px + thickness / 2, \
+ step * i + step / 2 - sample[i] * step / 2 + offset_px + thickness / 2, \
+ step * sample[i] - offset_px * 2 - thickness, \
+ step * sample[i] - offset_px * 2 - thickness, radius)
+ cr.fill() if fill else cr.stroke()
else:
step = width / ls
for i in range(ls):
set_source(cr, height, colors, sample[i] - 0.45)
offset_px = step * offset / 100 * sample[i]
+ if not fill:
+ offset_px += thickness / 2
draw_element(cr, \
step * i + step / 2 - sample[i] * step / 2 + offset_px, \
height / 2 - sample[i] * step / 2 + offset_px, \
step * sample[i] - offset_px * 2, \
step * sample[i] - offset_px * 2, radius)
- cr.fill()
+ cr.fill() if fill else cr.stroke()
-def bars(sample, cr, width, height, colors, offset):
+def bars(sample, cr, width, height, colors, offset, fill, thickness):
set_source(cr, height, colors)
ls = len(sample)
step = width / ls
offset_px = step * offset / 100
+ if fill:
+ thickness = 0
+ else:
+ thickness = min(thickness, (step - offset_px * 2) / 2)
+ cr.set_line_width(thickness)
for i in range(ls):
- cr.rectangle(step * i + offset_px, height - height * sample[i], \
- step - offset_px * 2, height)
- cr.fill()
+ cr.rectangle(step * i + offset_px + thickness / 2, \
+ height - height * sample[i] + thickness / 2, \
+ max(step - offset_px * 2 - thickness, 1), height * sample[i] - thickness)
+ cr.fill() if fill else cr.stroke()
diff --git a/src/drawing_area.py b/src/drawing_area.py
index e134d4a..ade9fae 100644
--- a/src/drawing_area.py
+++ b/src/drawing_area.py
@@ -31,7 +31,7 @@
from gi.repository import Gtk, GObject
from threading import Thread
from cavalier.cava import Cava
-from cavalier.draw_functions import wave, line, levels, particles, spine, bars
+from cavalier.draw_functions import wave, levels, particles, spine, bars
from cavalier.settings import CavalierSettings
class CavalierDrawingArea(Gtk.DrawingArea):
@@ -71,6 +71,7 @@ def on_settings_changed(self, key):
self.offset = self.settings['items-offset']
self.roundness = self.settings['items-roundness']
self.thickness = self.settings['line-thickness']
+ self.fill = self.settings['fill']
self.reverse_order = self.settings['reverse-order']
self.channels = self.settings['channels']
try:
@@ -97,22 +98,20 @@ def on_settings_changed(self, key):
def draw_func(self, area, cr, width, height, data, n):
if len(self.cava_sample) > 0:
if self.draw_mode == 'wave':
- wave(self.cava_sample, cr, width, height, self.colors)
- elif self.draw_mode == 'line':
- line(self.cava_sample, cr, width, height, self.colors, \
- self.thickness)
+ wave(self.cava_sample, cr, width, height, self.colors, \
+ self.fill, self.thickness)
elif self.draw_mode == 'levels':
levels(self.cava_sample, cr, width, height, self.colors, \
- self.offset, self.roundness)
+ self.offset, self.roundness, self.fill, self.thickness)
elif self.draw_mode == 'particles':
particles(self.cava_sample, cr, width, height, self.colors, \
- self.offset, self.roundness)
+ self.offset, self.roundness, self.fill, self.thickness)
elif self.draw_mode == 'spine':
spine(self.cava_sample, cr, width, height, self.colors, \
- self.offset, self.roundness)
+ self.offset, self.roundness, self.fill, self.thickness)
elif self.draw_mode == 'bars':
bars(self.cava_sample, cr, width, height, self.colors, \
- self.offset)
+ self.offset, self.fill, self.thickness)
def redraw(self):
self.queue_draw()
diff --git a/src/preferences_window.py b/src/preferences_window.py
index 0639a0d..04d98a3 100644
--- a/src/preferences_window.py
+++ b/src/preferences_window.py
@@ -66,14 +66,6 @@ def create_cavalier_page(self):
self.wave_row.set_activatable_widget(self.wave_check_btn)
self.cavalier_mode_group.add(self.wave_row)
- self.line_row = Adw.ActionRow.new()
- self.line_row.set_title(_('Line'))
- self.line_check_btn = Gtk.CheckButton.new()
- self.line_check_btn.set_group(self.wave_check_btn)
- self.line_row.add_prefix(self.line_check_btn)
- self.line_row.set_activatable_widget(self.line_check_btn)
- self.cavalier_mode_group.add(self.line_row)
-
self.levels_row = Adw.ActionRow.new()
self.levels_row.set_title(_('Levels'))
self.levels_check_btn = Gtk.CheckButton.new()
@@ -136,7 +128,7 @@ def create_cavalier_page(self):
self.pref_roundness = Adw.ActionRow.new()
self.pref_roundness.set_title(_('Roundness of items'))
self.pref_roundness.set_subtitle( \
- _('This setting only affects "levels" and "particles" modes.\n0 - square, 1 - round'))
+ _('This setting affects "levels", "particles" and "spine" modes.\n0 - square, 1 - round'))
self.pref_roundness_scale = Gtk.Scale.new_with_range( \
Gtk.Orientation.HORIZONTAL, 0.0, 1.0, 0.02)
self.pref_roundness_scale.set_size_request(190, -1)
@@ -145,10 +137,21 @@ def create_cavalier_page(self):
self.pref_roundness.add_suffix(self.pref_roundness_scale)
self.cavalier_group.add(self.pref_roundness)
+ self.pref_fill = Adw.ActionRow.new()
+ self.pref_fill.set_title(_('Filling'))
+ self.pref_fill.set_subtitle( \
+ _('Whether shapes should be filled or outlined.'))
+ self.pref_fill_switch = Gtk.Switch.new()
+ self.pref_fill_switch.set_valign(Gtk.Align.CENTER)
+ self.pref_fill.add_suffix(self.pref_fill_switch)
+ self.pref_fill.set_activatable_widget( \
+ self.pref_fill_switch)
+ self.cavalier_group.add(self.pref_fill)
+
self.pref_thickness = Adw.ActionRow.new()
self.pref_thickness.set_title(_('Thickness of lines'))
self.pref_thickness.set_subtitle( \
- _('Thickness of lines in "line" mode (in pixels).'))
+ _('Thickness of lines when filling is off (in pixels).'))
self.pref_thickness_scale = Gtk.Scale.new_with_range( \
Gtk.Orientation.HORIZONTAL, 1.0, 40.0, 1.0)
self.pref_thickness_scale.set_size_request(180, -1)
@@ -392,7 +395,7 @@ def create_colors_page(self):
self.bg_color_btns = []
def load_settings(self):
- (self.wave_check_btn, self.line_check_btn, self.levels_check_btn, \
+ (self.wave_check_btn, self.levels_check_btn, \
self.particles_check_btn, self.spine_check_btn, self.bars_check_btn)[ \
self.settings.get_range('mode')[1].index(self.settings['mode']) \
].set_active(True)
@@ -401,6 +404,7 @@ def load_settings(self):
self.pref_roundness_scale.set_value( \
round(self.settings['items-roundness'] / 50.0, 2))
self.pref_thickness_scale.set_value(self.settings['line-thickness'])
+ self.pref_fill_switch.set_active(self.settings['fill'])
self.pref_sharp_corners_switch.set_active( \
self.settings['sharp-corners'])
self.pref_show_controls_switch.set_active( \
@@ -447,7 +451,6 @@ def load_settings(self):
def bind_settings(self):
self.wave_check_btn.connect('toggled', self.change_mode, 'wave')
- self.line_check_btn.connect('toggled', self.change_mode, 'line')
self.levels_check_btn.connect('toggled', self.change_mode, 'levels')
self.particles_check_btn.connect('toggled', self.change_mode, \
'particles')
@@ -464,6 +467,11 @@ def bind_settings(self):
'line-thickness', self.pref_thickness_scale.get_value)
# `notify::state` signal returns additional parameter that
# we don't need, that's why lambda is used.
+ self.pref_fill_switch.connect('notify::state', \
+ lambda *args : self.save_setting(self.pref_fill_switch, \
+ 'fill', self.pref_fill_switch.get_state()))
+ # `notify::state` signal returns additional parameter that
+ # we don't need, that's why lambda is used.
self.pref_sharp_corners_switch.connect('notify::state', \
lambda *args : self.save_setting(self.pref_sharp_corners_switch, \
'sharp-corners', self.pref_sharp_corners_switch.get_state()))
diff --git a/src/shortcuts.py b/src/shortcuts.py
index e1966bc..411d8e9 100644
--- a/src/shortcuts.py
+++ b/src/shortcuts.py
@@ -110,6 +110,13 @@ def add_shortcuts(widget, settings):
Gtk.ShortcutTrigger.parse_string("T"), \
Gtk.NamedAction.new("cavalier.decrease-thickness")))
+ act_toggle_fill = Gio.SimpleAction.new("toggle-fill", None)
+ act_toggle_fill.connect('activate', toggle_setting, settings, 'fill')
+ action_map.add_action(act_toggle_fill)
+ shortcut_controller.add_shortcut(Gtk.Shortcut.new( \
+ Gtk.ShortcutTrigger.parse_string("F"), \
+ Gtk.NamedAction.new("cavalier.toggle-fill")))
+
act_toggle_corners = Gio.SimpleAction.new("toggle-corners", None)
act_toggle_corners.connect('activate', toggle_setting, settings, \
'sharp-corners')
diff --git a/src/shortcuts_dialog.ui b/src/shortcuts_dialog.ui
index 5ab05e8..1921148 100644
--- a/src/shortcuts_dialog.ui
+++ b/src/shortcuts_dialog.ui
@@ -34,6 +34,12 @@
R <Shift>R
+
+
+