2020-09-12 19:27:11 +00:00
#!/usr/bin/env python
2020-07-19 10:27:39 +00:00
import gi
import math
import cairo
import subprocess as sp
2020-07-19 11:20:08 +00:00
import sys
2020-09-12 19:27:11 +00:00
import os
from os . path import expanduser
2020-07-22 22:04:32 +00:00
import toml
2020-07-19 10:27:39 +00:00
gi . require_version ( " Gtk " , " 3.0 " )
gi . require_version ( " Gdk " , " 3.0 " )
2020-09-12 15:59:24 +00:00
gi . require_version ( " GtkLayerShell " , " 0.1 " )
2020-07-19 10:27:39 +00:00
from gi . repository import Gtk
from gi . repository import Gdk
2020-09-12 15:59:24 +00:00
from gi . repository import GtkLayerShell
2020-07-19 10:27:39 +00:00
2020-07-22 21:40:00 +00:00
Gtk . init ( )
2020-07-19 10:27:39 +00:00
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 ) :
2020-07-24 18:33:58 +00:00
def __init__ ( self , options , colors , config ) :
2020-07-19 10:27:39 +00:00
super ( ) . __init__ ( )
2020-07-22 22:05:06 +00:00
self . set_title ( " sike " )
2020-07-19 11:20:08 +00:00
self . options = options
2020-07-30 08:26:43 +00:00
self . all_options = options
self . path = [ ]
2020-07-19 19:13:36 +00:00
self . colors = colors
2020-07-24 18:33:58 +00:00
self . config = config
2020-07-19 10:27:39 +00:00
self . selected = None
self . set_resizable ( False )
self . set_decorated ( False )
self . mouse_x = 0
self . mouse_y = 0
2020-07-24 18:49:14 +00:00
self . connect ( " destroy " , Gtk . main_quit )
2020-07-19 10:27:39 +00:00
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 )
2020-09-12 16:08:01 +00:00
self . area . add_events ( Gdk . EventMask . TOUCH_MASK )
2020-07-19 10:27:39 +00:00
self . area . connect ( " draw " , self . draw_area )
2020-07-19 14:24:35 +00:00
self . area . connect ( " button_release_event " , self . on_selection )
2020-07-19 10:27:39 +00:00
self . area . connect ( " motion_notify_event " , self . on_move )
2020-09-12 16:08:01 +00:00
#self.area.connect("touch_event",self.on_touch)
2020-07-19 10:27:39 +00:00
self . add ( self . area )
2020-07-22 21:40:00 +00:00
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 )
2020-07-19 14:24:35 +00:00
def on_selection ( self , widget , data ) :
2020-07-19 11:29:55 +00:00
if self . selected != None :
2020-07-30 08:26:43 +00:00
self . path . append ( self . selected )
2020-07-19 11:29:55 +00:00
opt = self . options [ self . selected ]
if opt [ " type " ] == " shell " :
sp . Popen ( opt [ " command " ] . split ( " " ) )
2020-07-19 11:20:08 +00:00
self . destroy ( )
2020-07-19 11:29:55 +00:00
elif opt [ " type " ] == " internal " :
if opt [ " command " ] == " exit " :
self . destroy ( )
2020-07-19 14:24:35 +00:00
elif opt [ " type " ] == " i3 " :
if type ( i3 ) != None :
i3 . command ( f " [id= { focused . window } ] focus " )
i3 . command ( opt [ " command " ] )
self . destroy ( )
2020-07-19 11:29:55 +00:00
elif opt [ " type " ] == " submenu " :
self . options = opt [ " options " ]
self . selected = None
self . area . queue_draw ( )
else :
2020-07-30 08:26:43 +00:00
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 ]
2020-07-19 10:27:39 +00:00
def on_move ( self , widget , data ) :
self . area . queue_draw ( )
self . mouse_x = data . x
self . mouse_y = data . y
2020-07-24 18:49:14 +00:00
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
2020-07-19 10:27:39 +00:00
current = 0
2020-07-19 11:29:55 +00:00
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
2020-07-19 10:27:39 +00:00
2020-07-19 11:29:55 +00:00
def draw_option ( self , cr , i , option ) :
2020-07-19 11:20:08 +00:00
if self . selected != None :
if i == self . selected :
2020-07-19 19:13:36 +00:00
s = self . colors [ " selected " ]
cr . set_source_rgb ( s [ 0 ] , s [ 1 ] , s [ 2 ] )
2020-07-19 11:20:08 +00:00
else :
2020-07-19 19:13:36 +00:00
s = self . colors [ " deselected " ]
cr . set_source_rgb ( s [ 0 ] , s [ 1 ] , s [ 2 ] )
2020-07-19 11:20:08 +00:00
extents = cr . text_extents ( option [ " label " ] )
2020-07-19 11:29:55 +00:00
cr . move_to ( - self . width , 0 )
2020-07-19 11:20:08 +00:00
cr . show_text ( option [ " label " ] )
2020-07-19 11:29:55 +00:00
cr . move_to ( - self . width + extents . width , - extents . height / 4 )
cr . line_to ( - self . width / 8 , 0 )
2020-07-19 11:20:08 +00:00
cr . stroke ( )
2020-07-19 10:27:39 +00:00
def draw_area ( self , widget , cr ) :
self . lines = [ ]
width = widget . get_allocated_width ( )
height = widget . get_allocated_height ( )
2020-07-24 18:49:14 +00:00
2020-07-24 18:33:58 +00:00
bg = self . colors [ " background " ]
cr . set_source_rgb ( bg [ 0 ] , bg [ 1 ] , bg [ 2 ] )
cr . rectangle ( 0 , 0 , width , height )
2020-07-24 18:49:14 +00:00
cr . fill ( ) # Draw background
2020-07-19 10:27:39 +00:00
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 ] )
2020-07-24 18:33:58 +00:00
cr . select_font_face ( self . config [ " font " ] , cairo . FontSlant . NORMAL , cairo . FontWeight . BOLD )
2020-07-19 10:27:39 +00:00
cr . set_line_width ( 4 )
cr . set_font_size ( 20 )
2020-09-12 19:27:11 +00:00
start_angle = ( - math . pi / 12 ) * ( len ( self . options ) / 2 )
2020-07-19 10:27:39 +00:00
cr . rotate ( start_angle )
angle = start_angle
cr . set_source_rgb ( 1 , 1 , 1 )
for i , o in enumerate ( self . options ) :
2020-07-19 11:29:55 +00:00
self . draw_option ( cr , i , o )
2020-09-12 19:27:11 +00:00
cr . rotate ( math . pi / 12 )
2020-07-19 10:27:39 +00:00
self . lines . append ( angle )
2020-09-12 19:27:11 +00:00
angle + = math . pi / 12
config_home = os . getenv ( " XDG_CONFIG_HOME " , expanduser ( " ~/.config " ) )
2020-07-19 11:20:08 +00:00
2020-09-12 19:27:11 +00:00
config_file = toml . load ( open ( config_home + " /sike/config.toml " ) )
2020-07-19 10:27:39 +00:00
2020-09-12 19:27:11 +00:00
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 )
2020-07-19 11:20:08 +00:00
else :
2020-09-12 19:27:11 +00:00
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 ( )