1
// ItemPositionProvider.cs
3
// Copyright (C) 2009 GNOME Do
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program. If not, see <http://www.gnu.org/licenses/>.
20
using System.Collections.Generic;
21
using System.Collections.ObjectModel;
27
using Docky.Utilities;
29
namespace Docky.Interface
33
internal class ItemPositionProvider : IDisposable
36
List<Gdk.Point> static_positions;
38
ReadOnlyCollection<AbstractDockItem> DockItems {
39
get { return DockServices.ItemsService.DockItems; }
43
/// The width of the visible dock
45
public int DockWidth {
47
int val = 2 * HorizontalBuffer;
48
foreach (AbstractDockItem di in DockItems)
49
val += 2 * DockPreferences.IconBorderWidth + di.Width;
54
public int DockHeight {
56
return IconSize + 2 * VerticalBuffer;
60
public int VerticalBuffer {
64
public int HorizontalBuffer {
69
get { return DockPreferences.IconSize; }
73
get { return parent.Width; }
77
get { return parent.Height; }
81
get { return DockPreferences.ZoomSize; }
84
public Rectangle MinimumDockArea { get; private set; }
86
internal ItemPositionProvider(DockArea parent)
89
MinimumDockArea = CalculateMinimumArea ();
90
static_positions = new List<Gdk.Point> ();
95
void RegisterEvents ()
97
DockPreferences.IconSizeChanged += HandleIconSizeChanged;
98
DockServices.ItemsService.DockItemsChanged += HandleDockItemsChanged;
101
void UnregisterEvents ()
103
DockPreferences.IconSizeChanged -= HandleIconSizeChanged;
104
DockServices.ItemsService.DockItemsChanged -= HandleDockItemsChanged;
107
void HandleDockItemsChanged(IEnumerable<AbstractDockItem> items)
112
void HandleIconSizeChanged ()
117
Rectangle CalculateMinimumArea ()
120
widthOffset = (Width - DockWidth) / 2;
123
switch (DockPreferences.Orientation) {
124
case DockOrientation.Bottom:
125
rect = new Gdk.Rectangle (widthOffset, Height - DockHeight, DockWidth, DockHeight);
127
case DockOrientation.Top:
128
rect = new Gdk.Rectangle (widthOffset, 0, DockWidth, DockHeight);
131
rect = new Gdk.Rectangle (0, 0, 0, 0);
138
public void ForceUpdate ()
140
static_positions.Clear ();
141
MinimumDockArea = CalculateMinimumArea ();
144
public Rectangle DockArea (double zoomByEntryTime, Gdk.Point cursor)
146
Cairo.PointD startPosition, endPosition;
147
double start_zoom, end_zoom;
148
IconZoomedPosition (0, zoomByEntryTime, cursor, out startPosition, out start_zoom);
149
IconZoomedPosition (DockItems.Count - 1, zoomByEntryTime, cursor, out endPosition, out end_zoom);
151
int leftEdge, rightEdge, topEdge, bottomEdge;
152
double startEdgeConstant = start_zoom * (IconSize / 2) + (start_zoom * HorizontalBuffer) + DockPreferences.IconBorderWidth;
153
double endEdgeConstant = end_zoom * (IconSize / 2) + (end_zoom * HorizontalBuffer) + DockPreferences.IconBorderWidth;
155
switch (DockPreferences.Orientation) {
156
case DockOrientation.Bottom:
157
leftEdge = (int) (startPosition.X - startEdgeConstant);
158
rightEdge = (int) (endPosition.X + endEdgeConstant);
160
topEdge = Height - DockHeight;
162
case DockOrientation.Top:
163
leftEdge = (int) (startPosition.X - startEdgeConstant);
164
rightEdge = (int) (endPosition.X + endEdgeConstant);
165
bottomEdge = DockHeight;
169
leftEdge = rightEdge = topEdge = bottomEdge = 0;
173
Gdk.Rectangle rect = new Gdk.Rectangle (leftEdge,
175
Math.Abs (leftEdge - rightEdge),
176
Math.Abs (topEdge - bottomEdge));
181
public Gdk.Point IconUnzoomedPosition (int icon)
183
if (static_positions.Count != DockItems.Count) {
184
static_positions.Clear ();
186
for (int i = 0; i < DockItems.Count; i++) {
187
static_positions.Add (CalculateIconUnzoomedPosition (i));
191
if (DockItems.Count <= icon)
192
return CalculateIconUnzoomedPosition (icon);
194
return static_positions [icon];
197
private Gdk.Point CalculateIconUnzoomedPosition (int icon)
199
// the first icons center is at dock X + border + IconBorder + half its width
200
// it is subtle, but it *is* a mistake to add the half width until the end. adding
201
// premature will add the wrong width. It hurts the brain.
202
if (DockItems.Count <= icon)
203
return new Gdk.Point (0, 0);
205
int startOffset = HorizontalBuffer + DockPreferences.IconBorderWidth;
208
// this awkward structure is faster than the simpler implemenation by about 30%
209
// while this would normally mean nothing, this method sees lots of use and can
210
// afford a bit of ugly in exchange for a bit of speed.
212
foreach (AbstractDockItem di in DockItems) {
215
startOffset += di.Width;
219
startOffset += icon * 2 * DockPreferences.IconBorderWidth;
220
startOffset += DockItems [icon].Width >> 1;
222
switch (DockPreferences.Orientation) {
223
case DockOrientation.Bottom:
224
startOffset += MinimumDockArea.X;
225
return new Gdk.Point (startOffset, Height - (DockHeight >> 1));
227
case DockOrientation.Top:
228
startOffset += MinimumDockArea.X;
229
return new Gdk.Point (startOffset, DockHeight >> 1);
231
return new Gdk.Point (0, 0);
235
public void IconZoomedPosition (int icon, double zoomByEntryTime, Gdk.Point cursor, out Cairo.PointD position, out double zoom)
237
// get our actual center
238
Gdk.Point center = IconUnzoomedPosition (icon);
239
cursor = RebaseCursor (cursor);
241
double cursorOrientedPosition, centerOrientedPosition;
242
cursorOrientedPosition = cursor.X;
243
centerOrientedPosition = center.X;
245
// ZoomPercent is a number greater than 1. It should never be less than one.
246
// ZoomIn is a range of 0 to 1. we need a number that is 1 when ZoomIn is 0,
247
// and ZoomPercent when ZoomIn is 1. Then we treat this as
248
// if it were the ZoomPercent for the rest of the calculation
249
double zoomInPercent = 1 + (DockPreferences.ZoomPercent - 1) * zoomByEntryTime;
251
// offset from the center of the true position, ranged between 0 and half of the zoom range
252
double offset = Math.Min (Math.Abs (cursorOrientedPosition - centerOrientedPosition), ZoomSize >> 1);
257
double offsetPercent = offset / (ZoomSize / 2.0);
258
// zoom is calculated as 1 through target_zoom (default 2).
259
// The larger your offset, the smaller your zoom
261
// First we get the point on our curve that defines out current zoom
262
// offset is always going to fall on a point on the curve >= 0
263
zoom = 1 - offsetPercent * offsetPercent;
265
// scale this to match out zoomInPercent
266
zoom = 1 + zoom * (zoomInPercent - 1);
268
// pull in our offset to make things less spaced out
269
// explaination since this is a bit tricky...
270
// we have three terms, basically offset = f(x) * h(x) * g(x)
271
// f(x) == offset identify
272
// h(x) == a number from 0 to DockPreference.ZoomPercent - 1. This is used to get the smooth "zoom in" effect.
273
// additionally serves to "curve" the offset based on the max zoom
274
// g(x) == a term used to move the ends of the zoom inward. Precalculated that the edges should be 66% of the current
275
// value. The center is 100%. (1 - offsetPercent) == 0,1 distance from center
276
// The .66 value comes from the area under the curve. Dont as me to explain it too much because it's too clever for me
277
offset = offset * (zoomInPercent - 1) * (1 - offsetPercent / 3);
280
if (cursorOrientedPosition > centerOrientedPosition) {
281
centerOrientedPosition -= offset;
283
centerOrientedPosition += offset;
286
if (DockItems [icon].ScalingType == ScalingType.None) {
288
switch (DockPreferences.Orientation) {
289
case DockOrientation.Bottom:
290
position = new Cairo.PointD (centerOrientedPosition, center.Y);
292
case DockOrientation.Top:
293
position = new Cairo.PointD (centerOrientedPosition, center.Y);
296
position = new Cairo.PointD (0,0);
302
double zoomedCenterHeight = VerticalBuffer + DockItems [icon].Height * zoom / 2.0;
305
centerOrientedPosition = Math.Round (centerOrientedPosition);
307
switch (DockPreferences.Orientation) {
308
case DockOrientation.Bottom:
309
position = new Cairo.PointD (centerOrientedPosition, Height - zoomedCenterHeight);
311
case DockOrientation.Top:
312
position = new Cairo.PointD (centerOrientedPosition, zoomedCenterHeight);
315
position = new Cairo.PointD (0, 0);
320
public int IndexAtPosition (int x, int y)
322
return IndexAtPosition (new Gdk.Point (x, y));
325
public int IndexAtPosition (Gdk.Point location)
327
int position = location.X;
328
int startOffset = MinimumDockArea.X + HorizontalBuffer;
332
foreach (AbstractDockItem di in DockItems) {
333
width = di.Width + 2 * DockPreferences.IconBorderWidth;
334
if (position >= startOffset && position <= startOffset + width)
336
startOffset += width;
342
Gdk.Point RebaseCursor (Gdk.Point point)
344
int left = MinimumDockArea.X;
345
int right = left + MinimumDockArea.Width;
346
return new Gdk.Point (Math.Max (Math.Min (point.X, right), left), point.Y);
349
#region IDisposable implementation
351
public void Dispose ()