diff options
author | Alessandro Ghedini <al3xbio@gmail.com> | 2011-10-19 10:56:04 +0200 |
---|---|---|
committer | Alessandro Ghedini <al3xbio@gmail.com> | 2011-10-19 10:56:04 +0200 |
commit | 6451a495637c6e3047a5a56573cffc6e3de9a515 (patch) | |
tree | 7c3eb29532e7c4b36a9da13c5890664fb816959b /src/renderers |
Imported Upstream version 0.2+gitdfdad95upstream/0.2+gitdfdad95
Diffstat (limited to 'src/renderers')
-rw-r--r-- | src/renderers/centerRenderer.vala | 146 | ||||
-rw-r--r-- | src/renderers/pieRenderer.vala | 208 | ||||
-rw-r--r-- | src/renderers/pieWindow.vala | 263 | ||||
-rw-r--r-- | src/renderers/sliceRenderer.vala | 182 |
4 files changed, 799 insertions, 0 deletions
diff --git a/src/renderers/centerRenderer.vala b/src/renderers/centerRenderer.vala new file mode 100644 index 0000000..c30e9ce --- /dev/null +++ b/src/renderers/centerRenderer.vala @@ -0,0 +1,146 @@ +/* +Copyright (c) 2011 by Simon Schneegans + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +using GLib.Math; + +namespace GnomePie { + +// Renders the center of a Pie. + +public class CenterRenderer : GLib.Object { + + private unowned PieRenderer parent; + private unowned Image? caption; + private Color color; + + private AnimatedValue activity; + private AnimatedValue alpha; + + public CenterRenderer(PieRenderer parent) { + this.parent = parent; + this.activity = new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time); + this.alpha = new AnimatedValue.linear(0.0, 1.0, Config.global.theme.fade_in_time); + this.color = new Color(); + this.caption = null; + } + + public void fade_out() { + this.activity.reset_target(0.0, Config.global.theme.fade_out_time); + this.alpha.reset_target(0.0, Config.global.theme.fade_out_time); + } + + public void set_active_slice(SliceRenderer? active_slice) { + if (active_slice == null) { + this.activity.reset_target(0.0, Config.global.theme.transition_time); + } else { + this.activity.reset_target(1.0, Config.global.theme.transition_time); + this.caption = active_slice.caption; + this.color = active_slice.color; + } + } + + public void draw(double frame_time, Cairo.Context ctx, double angle, double distance) { + + var layers = Config.global.theme.center_layers; + + this.activity.update(frame_time); + this.alpha.update(frame_time); + + foreach (var layer in layers) { + + ctx.save(); + + double active_speed = (layer.active_rotation_mode == CenterLayer.RotationMode.TO_MOUSE) ? + 0.0 : layer.active_rotation_speed; + double inactive_speed = (layer.inactive_rotation_mode == CenterLayer.RotationMode.TO_MOUSE) ? + 0.0 : layer.inactive_rotation_speed; + double max_scale = layer.active_scale*this.activity.val + + layer.inactive_scale*(1.0-this.activity.val); + double max_alpha = layer.active_alpha*this.activity.val + + layer.inactive_alpha*(1.0-this.activity.val); + double colorize = ((layer.active_colorize == true) ? this.activity.val : 0.0) + + ((layer.inactive_colorize == true) ? 1.0 - this.activity.val : 0.0); + double max_rotation_speed = active_speed*this.activity.val + + inactive_speed*(1.0-this.activity.val); + CenterLayer.RotationMode rotation_mode = ((this.activity.val > 0.5) ? + layer.active_rotation_mode : layer.inactive_rotation_mode); + + if (rotation_mode == CenterLayer.RotationMode.TO_MOUSE) { + double diff = angle-layer.rotation; + max_rotation_speed = layer.active_rotation_speed*this.activity.val + + layer.inactive_rotation_speed*(1.0-this.activity.val); + double smoothy = fabs(diff) < 0.9 ? fabs(diff) + 0.1 : 1.0; + double step = max_rotation_speed*frame_time*smoothy; + + if (fabs(diff) <= step || fabs(diff) >= 2.0*PI - step) + layer.rotation = angle; + else { + if ((diff > 0 && diff < PI) || diff < -PI) layer.rotation += step; + else layer.rotation -= step; + } + + } else if (rotation_mode == CenterLayer.RotationMode.TO_ACTIVE) { + max_rotation_speed *= this.activity.val; + + double slice_angle = parent.slice_count() > 0 ? 2*PI/parent.slice_count() : 0; + double direction = (int)((angle+0.5*slice_angle) / (slice_angle))*slice_angle; + double diff = direction-layer.rotation; + double step = max_rotation_speed*frame_time; + + if (fabs(diff) <= step || fabs(diff) >= 2.0*PI - step) + layer.rotation = direction; + else { + if ((diff > 0 && diff < PI) || diff < -PI) layer.rotation += step; + else layer.rotation -= step; + } + + } else layer.rotation += max_rotation_speed*frame_time; + + layer.rotation = fmod(layer.rotation+2*PI, 2*PI); + + if (colorize > 0.0) ctx.push_group(); + + ctx.rotate(layer.rotation); + ctx.scale(max_scale, max_scale); + layer.image.paint_on(ctx, this.alpha.val*max_alpha); + + if (colorize > 0.0) { + ctx.set_operator(Cairo.Operator.ATOP); + ctx.set_source_rgb(this.color.r, this.color.g, this.color.b); + ctx.paint_with_alpha(colorize); + + ctx.set_operator(Cairo.Operator.OVER); + ctx.pop_group_to_source(); + ctx.paint(); + } + + ctx.restore(); + } + + // draw caption + if (Config.global.theme.caption && caption != null && this.activity.val > 0) { + ctx.save(); + ctx.identity_matrix(); + int pos = this.parent.get_size()/2; + ctx.translate(pos, (int)(Config.global.theme.caption_position) + pos); + caption.paint_on(ctx, this.activity.val*this.alpha.val); + ctx.restore(); + } + } +} + +} diff --git a/src/renderers/pieRenderer.vala b/src/renderers/pieRenderer.vala new file mode 100644 index 0000000..5b706f4 --- /dev/null +++ b/src/renderers/pieRenderer.vala @@ -0,0 +1,208 @@ +/* +Copyright (c) 2011 by Simon Schneegans + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +using GLib.Math; + +namespace GnomePie { + +///////////////////////////////////////////////////////////////////////// +/// This class renders a Pie. In order to accomplish that, it owns a +/// CenterRenderer and some SliceRenderers. +///////////////////////////////////////////////////////////////////////// + +public class PieRenderer : GLib.Object { + + public int quick_action { get; private set; } + public int active_slice { get; private set; } + public bool show_hotkeys { get; set; } + + private int size; + private Gee.ArrayList<SliceRenderer?> slices; + private CenterRenderer center; + private bool key_board_control = false; + + public PieRenderer() { + this.slices = new Gee.ArrayList<SliceRenderer?>(); + this.center = new CenterRenderer(this); + this.quick_action = -1; + this.active_slice = -2; + this.size = 0; + } + + public void load_pie(Pie pie) { + this.slices.clear(); + + int count = 0; + foreach (var group in pie.action_groups) { + foreach (var action in group.actions) { + var renderer = new SliceRenderer(this); + this.slices.add(renderer); + renderer.load(action, slices.size-1); + + if (action.is_quick_action) { + this.quick_action = count; + } + + ++count; + } + } + + this.set_highlighted_slice(this.quick_action); + + this.size = (int)fmax(2*Config.global.theme.radius + 2*Config.global.theme.slice_radius*Config.global.theme.max_zoom, + 2*Config.global.theme.center_radius); + + // increase size if there are many slices + if (slices.size > 0) { + this.size = (int)fmax(this.size, + (((Config.global.theme.slice_radius + Config.global.theme.slice_gap)/tan(PI/slices.size)) + + Config.global.theme.slice_radius)*2*Config.global.theme.max_zoom); + } + } + + public void activate() { + if (this.active_slice >= 0 && this.active_slice < this.slices.size) + slices[active_slice].activate(); + this.cancel(); + } + + public void cancel() { + foreach (var slice in this.slices) + slice.fade_out(); + + center.fade_out(); + } + + public void select_up() { + int bottom = this.slice_count()/4; + int top = this.slice_count()*3/4; + + if (this.active_slice == -1 || this.active_slice == bottom) + this.set_highlighted_slice(top); + else if (this.active_slice > bottom && this.active_slice < top) + this.set_highlighted_slice(this.active_slice+1); + else if (this.active_slice != top) + this.set_highlighted_slice((this.active_slice-1+this.slice_count())%this.slice_count()); + } + + public void select_down() { + int bottom = this.slice_count()/4; + int top = this.slice_count()*3/4; + + if (this.active_slice == -1 || this.active_slice == top) + this.set_highlighted_slice(bottom); + else if (this.active_slice > bottom && this.active_slice < top) + this.set_highlighted_slice(this.active_slice-1); + else if (this.active_slice != bottom) + this.set_highlighted_slice((this.active_slice+1)%this.slice_count()); + } + + public void select_left() { + int left = this.slice_count()/2; + int right = 0; + + if (this.active_slice == -1 || this.active_slice == right) + this.set_highlighted_slice(left); + else if (this.active_slice > left) + this.set_highlighted_slice(this.active_slice-1); + else if (this.active_slice < left) + this.set_highlighted_slice(this.active_slice+1); + } + + public void select_right() { + int left = this.slice_count()/2; + int right = 0; + + if (this.active_slice == -1 || this.active_slice == left) + this.set_highlighted_slice(right); + else if (this.active_slice > left) + this.set_highlighted_slice((this.active_slice+1)%this.slice_count()); + else if (this.active_slice < left && this.active_slice != right) + this.set_highlighted_slice((this.active_slice-1+this.slice_count())%this.slice_count()); + } + + public int slice_count() { + return slices.size; + } + + public int get_size() { + return size; + } + + public void draw(double frame_time, Cairo.Context ctx, int mouse_x, int mouse_y) { + double distance = sqrt(mouse_x*mouse_x + mouse_y*mouse_y); + double angle = 0.0; + + if (this.key_board_control) { + angle = 2.0*PI*this.active_slice/(double)slice_count(); + } else { + + if (distance > 0) { + angle = acos(mouse_x/distance); + if (mouse_y < 0) + angle = 2*PI - angle; + } + + int next_active_slice = this.active_slice; + + if (distance < Config.global.theme.active_radius + && this.quick_action >= 0 && this.quick_action < this.slices.size) { + + next_active_slice = this.quick_action; + angle = 2.0*PI*quick_action/(double)slice_count(); + } else if (distance > Config.global.theme.active_radius && this.slice_count() > 0) { + next_active_slice = (int)(angle*slices.size/(2*PI) + 0.5) % this.slice_count(); + } else { + next_active_slice = -1; + } + + this.set_highlighted_slice(next_active_slice); + } + + center.draw(frame_time, ctx, angle, distance); + + foreach (var slice in this.slices) + slice.draw(frame_time, ctx, angle, distance); + } + + public void on_mouse_move() { + this.key_board_control = false; + } + + public void set_highlighted_slice(int index) { + if (index != this.active_slice) { + if (index >= 0 && index < this.slice_count()) + this.active_slice = index; + else if (this.quick_action >= 0) + this.active_slice = this.quick_action; + else + this.active_slice = -1; + + SliceRenderer? active = (this.active_slice >= 0 && this.active_slice < this.slice_count()) ? + this.slices[this.active_slice] : null; + + center.set_active_slice(active); + + foreach (var slice in this.slices) + slice.set_active_slice(active); + + this.key_board_control = true; + } + } +} + +} diff --git a/src/renderers/pieWindow.vala b/src/renderers/pieWindow.vala new file mode 100644 index 0000000..c4ac2ec --- /dev/null +++ b/src/renderers/pieWindow.vala @@ -0,0 +1,263 @@ +/* +Copyright (c) 2011 by Simon Schneegans + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +using GLib.Math; + +namespace GnomePie { + +// An invisible window. Used to draw Pies onto. + +public class PieWindow : Gtk.Window { + + public signal void on_closing(); + + private PieRenderer renderer; + private bool closing = false; + private GLib.Timer timer; + + private bool has_compositing = false; + + private Image background = null; + + public PieWindow() { + this.renderer = new PieRenderer(); + + this.set_title("Gnome-Pie"); + this.set_skip_taskbar_hint(true); + this.set_skip_pager_hint(true); + this.set_keep_above(true); + this.set_type_hint(Gdk.WindowTypeHint.SPLASHSCREEN); + this.set_decorated(false); + this.set_resizable(false); + this.icon_name = "gnome-pie"; + this.set_accept_focus(false); + + if (this.screen.is_composited()) { + this.set_colormap(this.screen.get_rgba_colormap()); + this.has_compositing = true; + } + + this.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.KEY_RELEASE_MASK | + Gdk.EventMask.KEY_PRESS_MASK | + Gdk.EventMask.POINTER_MOTION_MASK); + + this.button_release_event.connect ((e) => { + if (e.button == 1) this.activate_slice(); + else this.cancel(); + return true; + }); + + // remember last pressed key in order to disable key repeat + uint last_key = 0; + this.key_press_event.connect((e) => { + if (e.keyval != last_key) { + last_key = e.keyval; + this.handle_key_press(e.keyval); + } + return true; + }); + + this.key_release_event.connect((e) => { + last_key = 0; + if (Config.global.turbo_mode) + this.activate_slice(); + else + this.handle_key_release(e.keyval); + return true; + }); + + this.motion_notify_event.connect((e) => { + this.renderer.on_mouse_move(); + return true; + }); + + this.expose_event.connect(this.draw); + } + + public void load_pie(Pie pie) { + this.renderer.load_pie(pie); + this.set_window_position(); + this.set_size_request(renderer.get_size(), renderer.get_size()); + } + + public void open() { + this.realize(); + + if (!this.has_compositing) { + int x, y, width, height; + this.get_position(out x, out y); + this.get_size(out width, out height); + this.background = new Image.capture_screen(x, y, width+1, height+1); + } + + this.show(); + this.fix_focus(); + + this.timer = new GLib.Timer(); + this.timer.start(); + this.queue_draw(); + + Timeout.add((uint)(1000.0/Config.global.refresh_rate), () => { + this.queue_draw(); + return this.visible; + }); + } + + private bool draw(Gtk.Widget da, Gdk.EventExpose event) { + // clear the window + var ctx = Gdk.cairo_create(this.window); + + if (this.has_compositing) { + ctx.set_operator (Cairo.Operator.CLEAR); + ctx.paint(); + ctx.set_operator (Cairo.Operator.OVER); + } else { + ctx.set_operator (Cairo.Operator.OVER); + ctx.set_source_surface(background.surface, -1, -1); + ctx.paint(); + } + + ctx.translate(this.width_request*0.5, this.height_request*0.5); + + double mouse_x = 0.0, mouse_y = 0.0; + this.get_pointer(out mouse_x, out mouse_y); + + double frame_time = this.timer.elapsed(); + this.timer.reset(); + + this.renderer.draw(frame_time, ctx, (int)(mouse_x - this.width_request*0.5), + (int)(mouse_y - this.height_request*0.5)); + + return true; + } + + private void activate_slice() { + if (!this.closing) { + this.closing = true; + this.on_closing(); + this.unfix_focus(); + this.renderer.activate(); + + Timeout.add((uint)(Config.global.theme.fade_out_time*1000), () => { + this.destroy(); + //ThemedIcon.clear_cache(); + return false; + }); + } + } + + private void cancel() { + if (!this.closing) { + this.closing = true; + this.on_closing(); + this.unfix_focus(); + this.renderer.cancel(); + + Timeout.add((uint)(Config.global.theme.fade_out_time*1000), () => { + this.destroy(); + //ThemedIcon.clear_cache(); + return false; + }); + } + } + + private void set_window_position() { + if(Config.global.open_at_mouse) this.set_position(Gtk.WindowPosition.MOUSE); + else this.set_position(Gtk.WindowPosition.CENTER); + } + + private void handle_key_press(uint key) { + if (Gdk.keyval_name(key) == "Escape") this.cancel(); + else if (Gdk.keyval_name(key) == "Return") this.activate_slice(); + else if (!Config.global.turbo_mode) { + if (Gdk.keyval_name(key) == "Up") this.renderer.select_up(); + else if (Gdk.keyval_name(key) == "Down") this.renderer.select_down(); + else if (Gdk.keyval_name(key) == "Left") this.renderer.select_left(); + else if (Gdk.keyval_name(key) == "Right") this.renderer.select_right(); + else if (Gdk.keyval_name(key) == "Alt_L") this.renderer.show_hotkeys = true; + else { + int index = -1; + + if (key >= 48 && key <= 57) index = (int)key - 48; + else if (key >= 97 && key <= 122) index = (int)key - 87; + else if (key >= 65 && key <= 90) index = (int)key - 55; + + if (index >= 0 && index < this.renderer.slice_count()) { + this.renderer.set_highlighted_slice(index); + + if (this.renderer.active_slice == index) { + GLib.Timeout.add((uint)(Config.global.theme.transition_time*1000.0), ()=> { + this.activate_slice(); + return false; + }); + } + + } + } + } + } + + private void handle_key_release(uint key) { + if (!Config.global.turbo_mode) { + if (Gdk.keyval_name(key) == "Alt_L") this.renderer.show_hotkeys = false; + } + } + + // utilities for grabbing focus + // Code from Gnome-Do/Synapse + private void fix_focus() { + uint32 timestamp = Gtk.get_current_event_time(); + this.present_with_time(timestamp); + this.get_window().raise(); + this.get_window().focus(timestamp); + + int i = 0; + Timeout.add(100, () => { + if (++i >= 100) return false; + return !try_grab_window(); + }); + } + + // Code from Gnome-Do/Synapse + private void unfix_focus() { + uint32 time = Gtk.get_current_event_time(); + Gdk.pointer_ungrab(time); + Gdk.keyboard_ungrab(time); + Gtk.grab_remove(this); + } + + // Code from Gnome-Do/Synapse + private bool try_grab_window() { + uint time = Gtk.get_current_event_time(); + if (Gdk.pointer_grab(this.get_window(), true, Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK, + null, null, time) == Gdk.GrabStatus.SUCCESS) { + + if (Gdk.keyboard_grab(this.get_window(), true, time) == Gdk.GrabStatus.SUCCESS) { + Gtk.grab_add(this); + return true; + } else { + Gdk.pointer_ungrab(time); + return false; + } + } + return false; + } +} + +} diff --git a/src/renderers/sliceRenderer.vala b/src/renderers/sliceRenderer.vala new file mode 100644 index 0000000..08c880f --- /dev/null +++ b/src/renderers/sliceRenderer.vala @@ -0,0 +1,182 @@ +/* +Copyright (c) 2011 by Simon Schneegans + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +using GLib.Math; + +namespace GnomePie { + +// Renders a Slice of a Pie. According to the current theme. + +public class SliceRenderer : GLib.Object { + + public bool active {get; private set; default = false;} + public Image caption {get; private set;} + public Color color {get; private set;} + + private Image active_icon; + private Image inactive_icon; + private Image hotkey; + + private Action action; + + private unowned PieRenderer parent; + private int position; + + private AnimatedValue fade; + private AnimatedValue scale; + private AnimatedValue alpha; + private AnimatedValue fade_rotation; + private AnimatedValue fade_scale; + + public SliceRenderer(PieRenderer parent) { + this.parent = parent; + + this.fade = new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time); + this.alpha = new AnimatedValue.linear(0.0, 1.0, Config.global.theme.fade_in_time); + this.scale = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, + 1.0/Config.global.theme.max_zoom, + 1.0/Config.global.theme.max_zoom, + Config.global.theme.transition_time, + Config.global.theme.springiness); + this.fade_scale = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, + Config.global.theme.fade_in_zoom, 1.0, + Config.global.theme.fade_in_time, + Config.global.theme.springiness); + this.fade_rotation = new AnimatedValue.cubic(AnimatedValue.Direction.OUT, + Config.global.theme.fade_in_rotation, 0.0, + Config.global.theme.fade_in_time); + } + + public void load(Action action, int position) { + this.position = position; + this.action = action; + + + if (Config.global.theme.caption) + this.caption = new RenderedText(action.name, + Config.global.theme.caption_width, + Config.global.theme.caption_height, + Config.global.theme.caption_font); + + this.active_icon = new ThemedIcon(action.icon, true); + this.inactive_icon = new ThemedIcon(action.icon, false); + + this.color = new Color.from_icon(this.active_icon); + + string hotkey_label = ""; + if (position < 10) { + hotkey_label = "%u".printf(position); + } else if (position < 36) { + hotkey_label = "%c".printf((char)(55 + position)); + } + + this.hotkey = new RenderedText(hotkey_label, (int)Config.global.theme.slice_radius*2, + (int)Config.global.theme.slice_radius*2, "sans 20"); + } + + public void activate() { + action.activate(); + } + + public void fade_out() { + this.alpha.reset_target(0.0, Config.global.theme.fade_out_time); + this.fade_scale = new AnimatedValue.cubic(AnimatedValue.Direction.IN, + this.fade_scale.val, + Config.global.theme.fade_out_zoom, + Config.global.theme.fade_out_time, + Config.global.theme.springiness); + this.fade_rotation = new AnimatedValue.cubic(AnimatedValue.Direction.IN, + this.fade_rotation.val, + Config.global.theme.fade_out_rotation, + Config.global.theme.fade_out_time); + } + + public void set_active_slice(SliceRenderer? active_slice) { + if (active_slice == this) { + this.fade.reset_target(1.0, Config.global.theme.transition_time); + } else { + this.fade.reset_target(0.0, Config.global.theme.transition_time); + } + } + + public void draw(double frame_time, Cairo.Context ctx, double angle, double distance) { + + double direction = 2.0 * PI * position/parent.slice_count() + this.fade_rotation.val; + double max_scale = 1.0/Config.global.theme.max_zoom; + double diff = fabs(angle-direction); + + if (diff > PI) + diff = 2 * PI - diff; + + if (diff < 2 * PI * Config.global.theme.zoom_range) + max_scale = (Config.global.theme.max_zoom/(diff * (Config.global.theme.max_zoom - 1) + /(2 * PI * Config.global.theme.zoom_range) + 1)) + /Config.global.theme.max_zoom; + + active = ((parent.active_slice >= 0) && (diff < PI/parent.slice_count())); + + max_scale = (parent.active_slice >= 0 ? max_scale : 1.0/Config.global.theme.max_zoom); + + if (fabs(this.scale.end - max_scale) > Config.global.theme.max_zoom*0.005) + this.scale.reset_target(max_scale, Config.global.theme.transition_time); + + this.scale.update(frame_time); + this.alpha.update(frame_time); + this.fade.update(frame_time); + this.fade_scale.update(frame_time); + this.fade_rotation.update(frame_time); + + ctx.save(); + + double radius = Config.global.theme.radius; + + if (atan((Config.global.theme.slice_radius+Config.global.theme.slice_gap) + /(radius/Config.global.theme.max_zoom)) > PI/parent.slice_count()) { + radius = (Config.global.theme.slice_radius+Config.global.theme.slice_gap) + /tan(PI/parent.slice_count())*Config.global.theme.max_zoom; + } + + ctx.scale(scale.val*fade_scale.val, scale.val*fade_scale.val); + ctx.translate(cos(direction)*radius, sin(direction)*radius); + + ctx.push_group(); + + ctx.set_operator(Cairo.Operator.ADD); + + if (fade.val > 0.0) active_icon.paint_on(ctx, this.alpha.val*this.fade.val); + if (fade.val < 1.0) inactive_icon.paint_on(ctx, this.alpha.val*(1.0 - fade.val)); + + if (this.parent.show_hotkeys) { + ctx.set_operator(Cairo.Operator.ATOP); + ctx.set_source_rgba(0, 0, 0, 0.5); + ctx.paint(); + } + + ctx.set_operator(Cairo.Operator.OVER); + + + ctx.pop_group_to_source(); + ctx.paint(); + + if (this.parent.show_hotkeys) + this.hotkey.paint_on(ctx, 1.0); + + ctx.restore(); + } +} + +} |