5
// JƩrƩmie Laval <jeremie.laval@xamarin.com>
7
// Copyright (c) 2012 Xamarin, Inc.
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34
namespace Xwt.GtkBackend
36
public class PopoverBackend : IPopoverBackend
38
class PopoverWindow : Gtk.Window
40
const int arrowPadding = 10;
44
Xwt.Popover.Position arrowPosition;
45
Gtk.Alignment alignment;
47
public PopoverWindow (Gtk.Widget child, Xwt.Popover.Position orientation) : base (WindowType.Toplevel)
49
this.AppPaintable = true;
50
this.Decorated = false;
51
this.SkipPagerHint = true;
52
this.SkipTaskbarHint = true;
53
this.TypeHint = Gdk.WindowTypeHint.PopupMenu;
54
//this.TransientFor = (Gtk.Window)child.Toplevel;
55
this.AddEvents ((int)Gdk.EventMask.FocusChangeMask);
56
//this.DefaultHeight = this.DefaultWidth = 400;
57
this.arrowPosition = orientation;
58
this.alignment = new Gtk.Alignment (0, 0, 1, 1);
60
this.alignment.Add (child);
61
this.FocusOutEvent += HandleFocusOutEvent;
62
OnScreenChanged (null);
65
public Xwt.Popover.Position ArrowPosition {
71
public void ReleaseInnerWidget ()
73
alignment.Remove (alignment.Child);
76
void HandleFocusOutEvent (object o, FocusOutEventArgs args)
81
public void SetPadding (WidgetSpacing spacing)
83
alignment.LeftPadding = radius + (uint) spacing.Left;
84
alignment.RightPadding = radius + (uint) spacing.Right;
85
if (arrowPosition == Xwt.Popover.Position.Top) {
86
alignment.TopPadding = radius + arrowPadding + (uint) spacing.Top;
87
alignment.BottomPadding = radius + (uint) spacing.Bottom;
89
alignment.BottomPadding = radius + arrowPadding + (uint) spacing.Bottom;
90
alignment.TopPadding = radius + (uint) spacing.Top;
94
protected override void OnScreenChanged (Gdk.Screen previous_screen)
96
// To check if the display supports alpha channels, get the colormap
97
var colormap = this.Screen.RgbaColormap;
98
if (colormap == null) {
99
colormap = this.Screen.RgbColormap;
100
supportAlpha = false;
104
this.Colormap = colormap;
105
base.OnScreenChanged (previous_screen);
108
protected override bool OnExposeEvent (Gdk.EventExpose evnt)
111
this.GdkWindow.GetSize (out w, out h);
112
var bounds = new Xwt.Rectangle (0.5, 0.5, w - 1, h - 1);
113
var backgroundColor = Xwt.Drawing.Color.FromBytes (230, 230, 230, 230);
114
var black = Xwt.Drawing.Color.FromBytes (60, 60, 60);
116
using (Context ctx = Gdk.CairoHelper.Create (this.GdkWindow)) {
117
// We clear the surface with a transparent color if possible
119
ctx.Color = new Color (1.0, 1.0, 1.0, 0.0);
121
ctx.Color = new Color (1.0, 1.0, 1.0);
122
ctx.Operator = Operator.Source;
125
var calibratedRect = RecalibrateChildRectangle (bounds);
126
// Fill it with one round rectangle
127
RoundRectangle (ctx, calibratedRect, radius);
129
ctx.Color = new Color (black.Red, black.Green, black.Blue, black.Alpha);
130
ctx.StrokePreserve ();
131
ctx.Color = new Color (backgroundColor.Red, backgroundColor.Green, backgroundColor.Blue, backgroundColor.Alpha);
135
// We first begin by positionning ourselves at the top-center or bottom center of the previous rectangle
136
var arrowX = bounds.Center.X;
137
var arrowY = arrowPosition == Xwt.Popover.Position.Top ? calibratedRect.Top + ctx.LineWidth : calibratedRect.Bottom - ctx.LineWidth;
139
ctx.MoveTo (arrowX, arrowY);
140
// We draw the rectangle path
143
ctx.Color = new Color (black.Red, black.Green, black.Blue, black.Alpha);
144
ctx.StrokePreserve ();
146
ctx.Color = new Color (backgroundColor.Red, backgroundColor.Green, backgroundColor.Blue, backgroundColor.Alpha);
150
base.OnExposeEvent (evnt);
154
void DrawTriangle (Context ctx)
156
var triangleSide = 2 * arrowPadding / Math.Sqrt (3);
157
var halfSide = triangleSide / 2;
158
var verticalModifier = arrowPosition == Xwt.Popover.Position.Top ? -1 : 1;
160
ctx.RelMoveTo (-halfSide, 0);
161
ctx.RelLineTo (halfSide, verticalModifier * arrowPadding);
162
ctx.RelLineTo (halfSide, verticalModifier * -arrowPadding);
165
void RoundRectangle (Context ctx, Rectangle rect, double radius)
167
double degrees = Math.PI / 180;
170
var height = rect.Height;
171
var width = rect.Width;
174
ctx.Arc (x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
175
ctx.Arc (x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
176
ctx.Arc (x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
177
ctx.Arc (x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
181
Xwt.Rectangle RecalibrateChildRectangle (Xwt.Rectangle bounds)
183
switch (arrowPosition) {
184
case Xwt.Popover.Position.Top:
185
return new Rectangle (bounds.X, bounds.Y + arrowPadding, bounds.Width, bounds.Height - arrowPadding);
186
case Xwt.Popover.Position.Bottom:
187
return new Rectangle (bounds.X, bounds.Y, bounds.Width, bounds.Height - arrowPadding);
193
PopoverWindow popover;
194
IPopoverEventSink sink;
197
public void Initialize (IPopoverEventSink sink)
202
public void InitializeBackend (object frontend)
204
this.frontend = (Popover) frontend;
207
public void EnableEvent (object eventId)
211
public void DisableEvent (object eventId)
215
public void Show (Xwt.Popover.Position orientation, Xwt.Widget reference, Xwt.Rectangle positionRect, Widget child)
217
var parent = reference.ParentWindow;
218
popover = new PopoverWindow ((Gtk.Widget)((WidgetBackend)Xwt.Engine.WidgetRegistry.GetBackend (child)).NativeWidget, orientation);
219
popover.SetPadding (frontend.Padding);
220
popover.TransientFor = ((WindowFrameBackend)Xwt.Engine.WidgetRegistry.GetBackend (parent)).Window;
221
popover.DestroyWithParent = true;
222
popover.Hidden += (o, args) => {
223
popover.ReleaseInnerWidget ();
228
var position = new Point (reference.ScreenBounds.Center.X, popover.ArrowPosition == Popover.Position.Top ? reference.ScreenBounds.Bottom : reference.ScreenBounds.Top);
230
popover.GrabFocus ();
232
popover.GetSize (out w, out h);
233
popover.Move ((int)position.X - w / 2, (int)position.Y);
234
popover.SizeAllocated += (o, args) => { popover.Move ((int)position.X - args.Allocation.Width / 2, (int)position.Y); popover.GrabFocus (); };
242
public void Dispose ()
b'\\ No newline at end of file'