187 lines
6.2 KiB
Python
Executable File
187 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import gi
|
|
import math
|
|
import cairo
|
|
import subprocess as sp
|
|
import sys
|
|
import os
|
|
from os.path import expanduser
|
|
import toml
|
|
gi.require_version("Gtk","3.0")
|
|
gi.require_version("Gdk","3.0")
|
|
gi.require_version("GtkLayerShell","0.1")
|
|
from gi.repository import Gtk
|
|
from gi.repository import Gdk
|
|
from gi.repository import GtkLayerShell
|
|
|
|
Gtk.init()
|
|
|
|
def point_on_circle(r, point, angle):
|
|
x = point[0] + r * math.cos(angle)
|
|
y = point[1] + r * math.sin(angle)
|
|
return [x,y]
|
|
|
|
def gradient(x1,y1,x2,y2):
|
|
return y1-y2/x1-x2
|
|
|
|
class MenuWindow(Gtk.Window):
|
|
def __init__(self, options, colors,config):
|
|
super().__init__()
|
|
self.set_title("sike")
|
|
self.options = options
|
|
self.all_options = options
|
|
self.path = []
|
|
self.colors = colors
|
|
self.config = config
|
|
self.selected = None
|
|
self.set_resizable(False)
|
|
self.set_decorated(False)
|
|
self.mouse_x = 0
|
|
self.mouse_y = 0
|
|
self.connect("destroy",Gtk.main_quit)
|
|
|
|
self.area = Gtk.DrawingArea()
|
|
self.area.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK)
|
|
self.area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
|
self.area.add_events(Gdk.EventMask.BUTTON_MOTION_MASK)
|
|
self.area.add_events(Gdk.EventMask.TOUCH_MASK)
|
|
self.area.connect("draw",self.draw_area)
|
|
self.area.connect("button_release_event",self.on_selection)
|
|
self.area.connect("motion_notify_event",self.on_move)
|
|
#self.area.connect("touch_event",self.on_touch)
|
|
self.add(self.area)
|
|
|
|
def scale(self,width,height):
|
|
self.width = width/3
|
|
self.height = height
|
|
self.set_default_size(width/3,height)
|
|
self.move(width-self.width,0)
|
|
|
|
def on_selection(self,widget,data):
|
|
if self.selected != None:
|
|
self.path.append(self.selected)
|
|
opt = self.options[self.selected]
|
|
if opt["type"] == "shell":
|
|
sp.Popen(opt["command"].split(" "))
|
|
self.destroy()
|
|
elif opt["type"] == "internal":
|
|
if opt["command"] == "exit":
|
|
self.destroy()
|
|
elif opt["type"] == "i3":
|
|
if type(i3) != None:
|
|
i3.command(f"[id={focused.window}] focus")
|
|
i3.command(opt["command"])
|
|
self.destroy()
|
|
elif opt["type"] == "submenu":
|
|
self.options = opt["options"]
|
|
self.selected = None
|
|
self.area.queue_draw()
|
|
else:
|
|
if self.path == []:
|
|
self.destroy()
|
|
else:
|
|
o = self.all_options
|
|
if len(self.path) == 1:
|
|
self.options = o
|
|
self.area.queue_draw()
|
|
else:
|
|
r = self.path[:-1].copy()
|
|
r.reverse()
|
|
for ind in r:
|
|
o = o[ind]
|
|
if type(o) == list:
|
|
self.options = o
|
|
else:
|
|
self.options = o["options"]
|
|
self.area.queue_draw()
|
|
del self.path[0]
|
|
|
|
def on_move(self,widget,data):
|
|
self.area.queue_draw()
|
|
self.mouse_x = data.x
|
|
self.mouse_y = data.y
|
|
mouse_angle = -(math.atan2(self.mouse_x-self.center[0],self.mouse_y-self.center[1]) + (math.pi/2)) # Get the angle from the center point of the menu to the mouse
|
|
current = 0
|
|
if self.mouse_x > self.width-(self.width/8):
|
|
self.selected = None
|
|
else:
|
|
for number, angle in enumerate(self.lines):
|
|
if mouse_angle < angle:
|
|
break
|
|
else:
|
|
current = number
|
|
self.selected = current
|
|
|
|
def draw_option(self, cr, i, option):
|
|
if self.selected != None:
|
|
if i == self.selected:
|
|
s = self.colors["selected"]
|
|
cr.set_source_rgb(s[0],s[1],s[2])
|
|
else:
|
|
s = self.colors["deselected"]
|
|
cr.set_source_rgb(s[0],s[1],s[2])
|
|
|
|
extents = cr.text_extents(option["label"])
|
|
cr.move_to(-self.width,0)
|
|
cr.show_text(option["label"])
|
|
cr.move_to(-self.width+extents.width,-extents.height/4)
|
|
cr.line_to(-self.width/8,0)
|
|
cr.stroke()
|
|
|
|
|
|
def draw_area(self, widget, cr):
|
|
self.lines = []
|
|
width = widget.get_allocated_width()
|
|
height = widget.get_allocated_height()
|
|
|
|
bg = self.colors["background"]
|
|
cr.set_source_rgb(bg[0],bg[1],bg[2])
|
|
cr.rectangle(0,0,width,height)
|
|
cr.fill() # Draw background
|
|
|
|
cr.translate(width,height/2) # All coordinates are relative to right center
|
|
cp = cr.get_current_point()
|
|
self.center = cr.user_to_device(cp[0],cp[1])
|
|
cr.select_font_face(self.config["font"],cairo.FontSlant.NORMAL,cairo.FontWeight.BOLD)
|
|
cr.set_line_width(4)
|
|
cr.set_font_size(20)
|
|
start_angle = (-math.pi/12)*(len(self.options)/2)
|
|
cr.rotate(start_angle)
|
|
angle = start_angle
|
|
cr.set_source_rgb(1,1,1)
|
|
for i, o in enumerate(self.options):
|
|
self.draw_option(cr, i, o)
|
|
cr.rotate(math.pi/12)
|
|
self.lines.append(angle)
|
|
angle += math.pi/12
|
|
|
|
config_home = os.getenv("XDG_CONFIG_HOME", expanduser("~/.config"))
|
|
|
|
config_file = toml.load(open(config_home + "/sike/config.toml"))
|
|
|
|
if config_file["config"]["use_i3"] == True:
|
|
from i3ipc import Connection
|
|
i3 = Connection()
|
|
focused = i3.get_tree().find_focused()
|
|
else:
|
|
i3 = None
|
|
|
|
win = MenuWindow(config_file["options"],config_file["colors"],config_file["config"])
|
|
screen = win.get_screen()
|
|
disp = screen.get_display()
|
|
monitor_count = disp.get_n_monitors()
|
|
if monitor_count == 1:
|
|
mon = disp.get_monitor(0)
|
|
else:
|
|
mon = disp.get_default_monitor()
|
|
g = mon.get_geometry()
|
|
win.scale(g.width,g.height)
|
|
GtkLayerShell.init_for_window(win)
|
|
GtkLayerShell.set_layer(win, GtkLayerShell.Layer.OVERLAY)
|
|
GtkLayerShell.set_anchor(win, GtkLayerShell.Edge.RIGHT, 1)
|
|
GtkLayerShell.set_anchor(win, GtkLayerShell.Edge.BOTTOM, 1)
|
|
GtkLayerShell.set_anchor(win, GtkLayerShell.Edge.TOP, 1)
|
|
win.show_all()
|
|
Gtk.main()
|