5
// Lluis Sanchez <lluis@xamarin.com>
7
// Copyright (c) 2011 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
28
using System.Collections.Generic;
34
using MonoMac.Foundation;
35
using MonoMac.ObjCRuntime;
42
public abstract class ViewBackend<T,S>: IWidgetBackend,IMacViewBackend where T:NSView where S:IWidgetEventSink
46
IViewObject viewObject;
47
WidgetEvent currentEvents;
50
void IBackend.InitializeBackend (object frontend)
52
this.frontend = (Widget) frontend;
53
if (viewObject != null)
54
viewObject.Frontend = (Widget) frontend;
57
void IWidgetBackend.Initialize (IWidgetEventSink sink)
63
public void SetAutosizeMode (bool autosize)
65
this.autosize = autosize;
71
public virtual void Initialize ()
76
get { return eventSink; }
79
IWidgetEventSink IMacViewBackend.EventSink {
80
get { return EventSink; }
83
public Widget Frontend {
89
public object NativeWidget {
96
get { return (T) ViewObject.View; }
99
public IViewObject ViewObject {
100
get { return viewObject; }
103
viewObject.Frontend = frontend;
107
public bool Visible {
108
get { return !Widget.Hidden; }
109
set { Widget.Hidden = !value; }
112
public virtual bool Sensitive {
117
public virtual bool CanGetFocus {
122
public virtual bool HasFocus {
123
get { return false; }
126
public void SetFocus ()
130
public string TooltipText {
132
return Widget.ToolTip;
135
Widget.ToolTip = value;
139
public void NotifyPreferredSizeChanged ()
141
EventSink.OnPreferredSizeChanged ();
144
public void SetCursor (CursorType cursor)
147
if (cursor == CursorType.Arrow)
148
ctype = NSCursor.ArrowCursor;
149
else if (cursor == CursorType.Crosshair)
150
ctype = NSCursor.CrosshairCursor;
151
else if (cursor == CursorType.Hand)
152
ctype = NSCursor.ClosedHandCursor;
153
else if (cursor == CursorType.IBeam)
154
ctype = NSCursor.IBeamCursor;
155
else if (cursor == CursorType.ResizeDown)
156
ctype = NSCursor.ResizeDownCursor;
157
else if (cursor == CursorType.ResizeUp)
158
ctype = NSCursor.ResizeUpCursor;
159
else if (cursor == CursorType.ResizeLeft)
160
ctype = NSCursor.ResizeLeftCursor;
161
else if (cursor == CursorType.ResizeRight)
162
ctype = NSCursor.ResizeRightCursor;
163
else if (cursor == CursorType.ResizeLeftRight)
164
ctype = NSCursor.ResizeLeftRightCursor;
165
else if (cursor == CursorType.ResizeUpDown)
166
ctype = NSCursor.ResizeUpDownCursor;
168
ctype = NSCursor.ArrowCursor;
169
// TODO: assign the cursor
177
public void Dispose ()
179
GC.SuppressFinalize (this);
183
protected virtual void Dispose (bool disposing)
187
public virtual SizeRequestMode SizeRequestMode {
188
get { return SizeRequestMode.HeightForWidth; }
191
Size IWidgetBackend.Size {
192
get { return new Size (Widget.WidgetWidth (), Widget.WidgetHeight ()); }
195
NSView IMacViewBackend.View {
196
get { return (NSView) Widget; }
199
public static NSView GetWidget (IWidgetBackend w)
201
return ((IMacViewBackend)w).View;
204
public static NSView GetWidget (Widget w)
206
return GetWidget ((IWidgetBackend)WidgetRegistry.GetBackend (w));
209
public virtual object Font {
211
if (Widget is NSControl)
212
return ((NSControl)(object)Widget).Font;
213
if (Widget is NSText)
214
return ((NSText)(object)Widget).Font;
215
return NSFont.ControlContentFontOfSize (NSFont.SystemFontSize);
218
if (Widget is NSControl)
219
((NSControl)(object)Widget).Font = (NSFont) value;
220
if (Widget is NSText)
221
((NSText)(object)Widget).Font = (NSFont) value;
225
public virtual Xwt.Drawing.Color BackgroundColor {
230
#region IWidgetBackend implementation
232
public Point ConvertToScreenCoordinates (Point widgetCoordinates)
234
var lo = Widget.ConvertPointToBase (new PointF ((float)widgetCoordinates.X, (float)widgetCoordinates.Y));
235
lo = Widget.Window.ConvertBaseToScreen (lo);
236
return new Point (lo.X, lo.Y);
239
protected virtual Size GetNaturalSize ()
241
// double w1 = Widget.FittingSize.Width;
242
return new Size (Widget.WidgetWidth(), Widget.WidgetHeight ());
245
public WidgetSize GetPreferredWidth ()
247
var w = GetNaturalSize ().Width;
248
var s = new Xwt.WidgetSize (w, w);
249
if (minWidth != -1 && s.MinSize > minWidth)
250
s.MinSize = minWidth;
254
public WidgetSize GetPreferredHeight ()
256
var h = GetNaturalSize ().Height;
257
var s = new Xwt.WidgetSize (h, h);
258
if (minHeight != -1 && s.MinSize > minHeight)
259
s.MinSize = minHeight;
263
public WidgetSize GetPreferredHeightForWidth (double width)
265
return GetPreferredHeight ();
268
public WidgetSize GetPreferredWidthForHeight (double height)
270
return GetPreferredWidth ();
273
double minWidth = -1, minHeight = -1;
275
public void SetMinSize (double width, double height)
281
public void SetNaturalSize (double width, double height)
286
public virtual void UpdateLayout ()
288
var m = Frontend.Margin;
289
Widget.SetBoundsOrigin (new PointF (-(float)m.Left, -(float)m.Top));
294
void AutoUpdateSize ()
296
var ws = Frontend.Surface.GetPreferredWidth ();
297
var h = Frontend.Surface.GetPreferredHeightForWidth (ws.NaturalSize);
298
Widget.SetWidgetBounds (new Rectangle (0, 0, ws.NaturalSize, h.NaturalSize));
301
public virtual void EnableEvent (object eventId)
303
if (eventId is WidgetEvent) {
304
WidgetEvent ev = (WidgetEvent) eventId;
309
public virtual void DisableEvent (object eventId)
311
if (eventId is WidgetEvent) {
312
WidgetEvent ev = (WidgetEvent) eventId;
313
currentEvents &= ~ev;
317
static Selector draggingEnteredSel = new Selector ("draggingEntered:");
318
static Selector draggingUpdatedSel = new Selector ("draggingUpdated:");
319
static Selector draggingExitedSel = new Selector ("draggingExited:");
320
static Selector prepareForDragOperationSel = new Selector ("prepareForDragOperation:");
321
static Selector performDragOperationSel = new Selector ("performDragOperation:");
322
static Selector concludeDragOperationSel = new Selector ("concludeDragOperation:");
323
static HashSet<Type> typesConfiguredForDragDrop = new HashSet<Type> ();
325
static void SetupForDragDrop (Type type)
327
lock (typesConfiguredForDragDrop) {
328
if (typesConfiguredForDragDrop.Add (type)) {
329
Class c = new Class (type);
330
c.AddMethod (draggingEnteredSel.Handle, new Func<IntPtr,IntPtr,IntPtr,NSDragOperation> (DraggingEntered), "i@:@");
331
c.AddMethod (draggingUpdatedSel.Handle, new Func<IntPtr,IntPtr,IntPtr,NSDragOperation> (DraggingUpdated), "i@:@");
332
c.AddMethod (draggingExitedSel.Handle, new Action<IntPtr,IntPtr,IntPtr> (DraggingExited), "v@:@");
333
c.AddMethod (prepareForDragOperationSel.Handle, new Func<IntPtr,IntPtr,IntPtr,bool> (PrepareForDragOperation), "B@:@");
334
c.AddMethod (performDragOperationSel.Handle, new Func<IntPtr,IntPtr,IntPtr,bool> (PerformDragOperation), "B@:@");
335
c.AddMethod (concludeDragOperationSel.Handle, new Action<IntPtr,IntPtr,IntPtr> (ConcludeDragOperation), "v@:@");
340
public void DragStart (DragStartData sdata)
342
var lo = Widget.ConvertPointToBase (new PointF (Widget.Bounds.X, Widget.Bounds.Y));
343
lo = Widget.Window.ConvertBaseToScreen (lo);
344
var ml = NSEvent.CurrentMouseLocation;
345
var pb = NSPasteboard.FromName (NSPasteboard.NSDragPasteboardName);
347
throw new InvalidOperationException ("Could not get pasteboard");
348
if (sdata.Data == null)
349
throw new ArgumentNullException ("data");
350
InitPasteboard (pb, sdata.Data);
351
var img = (NSImage)sdata.ImageBackend;
352
var pos = new PointF (ml.X - lo.X - (float)sdata.HotX, lo.Y - ml.Y - (float)sdata.HotY + img.Size.Height);
353
Widget.DragImage (img, pos, new SizeF (0, 0), NSApplication.SharedApplication.CurrentEvent, pb, Widget, true);
356
public void SetDragSource (TransferDataType[] types, DragDropAction dragAction)
360
public void SetDragTarget (TransferDataType[] types, DragDropAction dragAction)
362
SetupForDragDrop (Widget.GetType ());
363
var dtypes = types.Select (t => ToNSDragType (t)).ToArray ();
364
Widget.RegisterForDraggedTypes (dtypes);
367
static NSDragOperation DraggingEntered (IntPtr sender, IntPtr sel, IntPtr dragInfo)
369
return DraggingUpdated (sender, sel, dragInfo);
372
static NSDragOperation DraggingUpdated (IntPtr sender, IntPtr sel, IntPtr dragInfo)
374
IViewObject ob = Runtime.GetNSObject (sender) as IViewObject;
376
return NSDragOperation.None;
377
var backend = (ViewBackend<T,S>) WidgetRegistry.GetBackend (ob.Frontend);
379
NSDraggingInfo di = new NSDraggingInfo (dragInfo);
380
var types = di.DraggingPasteboard.Types.Select (t => ToXwtDragType (t)).ToArray ();
381
var pos = new Point (di.DraggingLocation.X, di.DraggingLocation.Y);
383
if ((backend.currentEvents & WidgetEvent.DragOverCheck) != 0) {
384
var args = new DragOverCheckEventArgs (pos, types, ConvertAction (di.DraggingSourceOperationMask));
385
backend.OnDragOverCheck (di, args);
386
if (args.AllowedAction == DragDropAction.None)
387
return NSDragOperation.None;
388
if (args.AllowedAction != DragDropAction.Default)
389
return ConvertAction (args.AllowedAction);
392
if ((backend.currentEvents & WidgetEvent.DragOver) != 0) {
393
TransferDataStore store = new TransferDataStore ();
394
FillDataStore (store, di.DraggingPasteboard, ob.View.RegisteredDragTypes ());
395
var args = new DragOverEventArgs (pos, store, ConvertAction (di.DraggingSourceOperationMask));
396
backend.OnDragOver (di, args);
397
if (args.AllowedAction == DragDropAction.None)
398
return NSDragOperation.None;
399
if (args.AllowedAction != DragDropAction.Default)
400
return ConvertAction (args.AllowedAction);
403
return di.DraggingSourceOperationMask;
406
static void DraggingExited (IntPtr sender, IntPtr sel, IntPtr dragInfo)
408
IViewObject ob = Runtime.GetNSObject (sender) as IViewObject;
410
var backend = (ViewBackend<T,S>) WidgetRegistry.GetBackend (ob.Frontend);
411
Toolkit.Invoke (delegate {
412
backend.eventSink.OnDragLeave (EventArgs.Empty);
417
static bool PrepareForDragOperation (IntPtr sender, IntPtr sel, IntPtr dragInfo)
419
IViewObject ob = Runtime.GetNSObject (sender) as IViewObject;
423
var backend = (ViewBackend<T,S>) WidgetRegistry.GetBackend (ob.Frontend);
425
NSDraggingInfo di = new NSDraggingInfo (dragInfo);
426
var types = di.DraggingPasteboard.Types.Select (t => ToXwtDragType (t)).ToArray ();
427
var pos = new Point (di.DraggingLocation.X, di.DraggingLocation.Y);
429
if ((backend.currentEvents & WidgetEvent.DragDropCheck) != 0) {
430
var args = new DragCheckEventArgs (pos, types, ConvertAction (di.DraggingSourceOperationMask));
431
bool res = Toolkit.Invoke (delegate {
432
backend.eventSink.OnDragDropCheck (args);
434
if (args.Result == DragDropResult.Canceled || !res)
440
static bool PerformDragOperation (IntPtr sender, IntPtr sel, IntPtr dragInfo)
442
IViewObject ob = Runtime.GetNSObject (sender) as IViewObject;
446
var backend = (ViewBackend<T,S>) WidgetRegistry.GetBackend (ob.Frontend);
448
NSDraggingInfo di = new NSDraggingInfo (dragInfo);
449
var pos = new Point (di.DraggingLocation.X, di.DraggingLocation.Y);
451
if ((backend.currentEvents & WidgetEvent.DragDrop) != 0) {
452
TransferDataStore store = new TransferDataStore ();
453
FillDataStore (store, di.DraggingPasteboard, ob.View.RegisteredDragTypes ());
454
var args = new DragEventArgs (pos, store, ConvertAction (di.DraggingSourceOperationMask));
455
Toolkit.Invoke (delegate {
456
backend.eventSink.OnDragDrop (args);
463
static void ConcludeDragOperation (IntPtr sender, IntPtr sel, IntPtr dragInfo)
465
Console.WriteLine ("ConcludeDragOperation");
468
protected virtual void OnDragOverCheck (NSDraggingInfo di, DragOverCheckEventArgs args)
470
Toolkit.Invoke (delegate {
471
eventSink.OnDragOverCheck (args);
475
protected virtual void OnDragOver (NSDraggingInfo di, DragOverEventArgs args)
477
Toolkit.Invoke (delegate {
478
eventSink.OnDragOver (args);
482
void InitPasteboard (NSPasteboard pb, TransferDataSource data)
485
foreach (var t in data.DataTypes) {
486
if (t == TransferDataType.Text) {
487
pb.AddTypes (new string[] { NSPasteboard.NSStringType }, null);
488
pb.SetStringForType ((string)data.GetValue (t), NSPasteboard.NSStringType);
493
static void FillDataStore (TransferDataStore store, NSPasteboard pb, string[] types)
495
foreach (var t in types) {
496
if (!pb.Types.Contains (t))
498
if (t == NSPasteboard.NSStringType)
499
store.AddText (pb.GetStringForType (t));
500
else if (t == NSPasteboard.NSFilenamesType) {
501
string data = pb.GetStringForType (t);
502
XmlDocument doc = new XmlDocument ();
503
doc.XmlResolver = null; // Avoid DTD validation
505
store.AddUris (doc.SelectNodes ("/plist/array/string").Cast<XmlElement> ().Select (e => new Uri (e.InnerText)).ToArray ());
510
static NSDragOperation ConvertAction (DragDropAction action)
512
NSDragOperation res = (NSDragOperation)0;
513
if ((action & DragDropAction.Copy) != 0)
514
res |= NSDragOperation.Copy;
515
if ((action & DragDropAction.Move) != 0)
516
res |= NSDragOperation.Move;
517
if ((action & DragDropAction.Link) != 0)
518
res |= NSDragOperation.Link;
522
static DragDropAction ConvertAction (NSDragOperation action)
524
if (action == NSDragOperation.AllObsolete)
525
return DragDropAction.All;
526
DragDropAction res = (DragDropAction)0;
527
if ((action & NSDragOperation.Copy) != 0)
528
res |= DragDropAction.Copy;
529
if ((action & NSDragOperation.Move) != 0)
530
res |= DragDropAction.Move;
531
if ((action & NSDragOperation.Link) != 0)
532
res |= DragDropAction.Link;
536
static string ToNSDragType (TransferDataType type)
538
if (type == TransferDataType.Text) return NSPasteboard.NSStringType;
539
if (type == TransferDataType.Uri) return NSPasteboard.NSFilenamesType;
540
if (type == TransferDataType.Image) return NSPasteboard.NSPictType;
541
if (type == TransferDataType.Rtf) return NSPasteboard.NSRtfType;
545
static TransferDataType ToXwtDragType (string type)
547
if (type == NSPasteboard.NSStringType)
548
return TransferDataType.Text;
549
if (type == NSPasteboard.NSFilenamesType)
550
return TransferDataType.Uri;
551
if (type == NSPasteboard.NSPictType)
552
return TransferDataType.Image;
553
if (type == NSPasteboard.NSRtfType)
554
return TransferDataType.Rtf;
555
return TransferDataType.FromId (type);
561
public interface IMacViewBackend
564
Widget Frontend { get; }
565
void NotifyPreferredSizeChanged ();
566
IWidgetEventSink EventSink { get; }
568
// To be called when the widget is a root and is not inside a Xwt window. For example, when it is in a popover or a tooltip
569
// In that case, the widget has to listen to the change event of the children and resize itself
570
void SetAutosizeMode (bool autosize);