diff options
Diffstat (limited to 'src/renderers/sliceRenderer.vala')
-rw-r--r-- | src/renderers/sliceRenderer.vala | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/src/renderers/sliceRenderer.vala b/src/renderers/sliceRenderer.vala new file mode 100644 index 0000000..591fbdd --- /dev/null +++ b/src/renderers/sliceRenderer.vala @@ -0,0 +1,295 @@ +///////////////////////////////////////////////////////////////////////// +// Copyright (c) 2011-2015 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 { + + ///////////////////////////////////////////////////////////////////// + /// Whether this slice is active (hovered) or not. + ///////////////////////////////////////////////////////////////////// + + public bool active {get; private set; default = false;} + + ///////////////////////////////////////////////////////////////////// + /// The Image which should be displayed as center caption when this + /// slice is active. + ///////////////////////////////////////////////////////////////////// + + public Image caption {get; private set;} + + ///////////////////////////////////////////////////////////////////// + /// The color which should be used for colorizing center layers when + /// this slice is active. + ///////////////////////////////////////////////////////////////////// + + public Color color {get; private set;} + + ///////////////////////////////////////////////////////////////////// + /// The Action which is rendered by this SliceRenderer. + ///////////////////////////////////////////////////////////////////// + + public Action action; + + ///////////////////////////////////////////////////////////////////// + /// The two Images used, when this slice is active or not. + ///////////////////////////////////////////////////////////////////// + + private Image active_icon; + private Image inactive_icon; + + ///////////////////////////////////////////////////////////////////// + /// The Image displaying the associated hot key of this slice. + ///////////////////////////////////////////////////////////////////// + + private Image hotkey; + + ///////////////////////////////////////////////////////////////////// + /// The PieRenderer which owns this SliceRenderer. + ///////////////////////////////////////////////////////////////////// + + private unowned PieRenderer parent; + + ///////////////////////////////////////////////////////////////////// + /// The index of this slice in a pie. Clockwise assigned, starting + /// from the right-most slice. + ///////////////////////////////////////////////////////////////////// + + private int position; + + ///////////////////////////////////////////////////////////////////// + /// AnimatedValues needed for a slice. + ///////////////////////////////////////////////////////////////////// + + private AnimatedValue fade; // for transitions from active to inactive + private AnimatedValue scale; // for zoom effect + private AnimatedValue alpha; // for fading in/out + private AnimatedValue fade_rotation; // for fading in/out + private AnimatedValue fade_scale; // for fading in/out + private AnimatedValue wobble; // for organic wobbling + + ///////////////////////////////////////////////////////////////////// + /// C'tor, initializes all AnimatedValues. + ///////////////////////////////////////////////////////////////////// + + public SliceRenderer(PieRenderer parent) { + this.parent = parent; + this.reset_anim(); + } + + ///////////////////////////////////////////////////////////////////// + /// Put all AnimatedValues in their initial values + ///////////////////////////////////////////////////////////////////// + + public void reset_anim() { + this.fade = new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time); + this.wobble = 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); + } + + ///////////////////////////////////////////////////////////////////// + /// Loads an Action. All members are initialized accordingly. + ///////////////////////////////////////////////////////////////////// + + 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, + Config.global.theme.caption_color, + Config.global.global_scale); + + this.active_icon = new ThemedIcon(action.name, action.icon, true); + this.inactive_icon = new ThemedIcon(action.name, action.icon, false); + + this.color = new Color.from_icon(this.active_icon); + + string hotkey_label = ""; + if (position < 10) { + hotkey_label = "%u".printf((position+1)%10); + } 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", + new Color(), Config.global.global_scale); + } + + ///////////////////////////////////////////////////////////////////// + /// Activates the Action of this slice. + ///////////////////////////////////////////////////////////////////// + + public void activate(uint32 time_stamp) { + action.activate(time_stamp); + } + + ///////////////////////////////////////////////////////////////////// + /// Initiates the fade-out animation by resetting the targets of the + /// AnimatedValues to 0.0. + ///////////////////////////////////////////////////////////////////// + + 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); + } + + ///////////////////////////////////////////////////////////////////// + /// Should be called if the active slice of the PieRenderer changes. + /// The members activity, caption and color are set accordingly. + ///////////////////////////////////////////////////////////////////// + + 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); + } + } + + ///////////////////////////////////////////////////////////////////// + /// Draws all layers of the slice. + ///////////////////////////////////////////////////////////////////// + + public void draw(double frame_time, Cairo.Context ctx, double angle, int slice_track) { + + // update the AnimatedValues + 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); + this.wobble.update(frame_time); + + double direction = 2.0 * PI * (position-parent.first_slice_idx)/parent.total_slice_count + + parent.first_slice_angle + this.fade_rotation.val; + double max_scale = 1.0/Config.global.theme.max_zoom; + double diff = fabs(angle-direction); + + if (diff > 2 * PI) { + diff = diff - 2 * PI; + } + + if (diff > PI) { + diff = 2 * PI - diff; + } + + + active = ((parent.active_slice >= 0) && (diff < PI/parent.total_slice_count)); + + if (slice_track != 0) { + double wobble = Config.global.theme.wobble*diff/PI*(1-diff/PI); + if ((direction < angle && direction > angle - PI) || direction > PI+angle) { + this.wobble.reset_target(-wobble, Config.global.theme.transition_time*0.5); + } else { + this.wobble.reset_target(wobble, Config.global.theme.transition_time*0.5); + } + } else { + this.wobble.reset_target(0, Config.global.theme.transition_time*0.5); + } + + direction += this.wobble.val; + + 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; + + + + max_scale = (slice_track != 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); + + ctx.save(); + + // distance from the center + double radius = Config.global.theme.radius; + + // increase radius if there are many slices in a pie + if (atan((Config.global.theme.slice_radius+Config.global.theme.slice_gap) + /(radius/Config.global.theme.max_zoom)) > PI/parent.total_slice_count) { + radius = (Config.global.theme.slice_radius+Config.global.theme.slice_gap) + /tan(PI/parent.total_slice_count)*Config.global.theme.max_zoom; + } + + // transform the context + 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); + + // paint the images + 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(); + + // draw hotkeys if necassary + if (this.parent.show_hotkeys) { + this.hotkey.paint_on(ctx, 1.0); + } + + ctx.restore(); + } +} + +} |