diff options
author | iain holmes <iain@xamarin.com> | 2017-01-09 14:30:29 +0300 |
---|---|---|
committer | iain holmes <iain@xamarin.com> | 2017-01-09 14:30:29 +0300 |
commit | e69fb4e2a6cee1336aa965da0046e337354987ff (patch) | |
tree | 6423a5d4649495340834b6dbc123d7dba6d77e80 /main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper | |
parent | d0822049b5ad9e55d0c77b3c22e852b074345541 (diff) |
[A11y] Tidy up the AtkCocoaHelpers
Separate AtkCocoaHelper into separate files for Mac and Noop
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper')
3 files changed, 1302 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelper.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelper.cs new file mode 100644 index 0000000000..ce0a139e58 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelper.cs @@ -0,0 +1,694 @@ +// +// AtkCocoaHelper.cs +// +// Author: +// Iain Holmes <iain@xamarin.com> +// +// Copyright (c) 2016 Xamarin, Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +#if MAC +using AppKit; +using CoreGraphics; +using Foundation; +using ObjCRuntime; +#endif + +namespace MonoDevelop.Components.AtkCocoaHelper +{ + // AtkCocoaHelper wraps NSAccessibilityElement to set NSAccessibility properties that aren't supported by Atk + public static class AtkCocoa + { + public enum Actions + { + AXCancel, + AXConfirm, + AXDecrement, + AXDelete, + AXIncrement, + AXPick, + AXPress, + AXRelease, + AXShowAlternateUI, + AXShowDefaultUI, + AXShowMenu + }; + + public enum Roles + { + AXButton, + AXCell, + AXColumn, + AXGroup, + AXImage, + AXMenuButton, + AXRadioButton, + AXRow, + AXRuler, + AXSplitGroup, + AXSplitter, + AXStaticText, + AXTabGroup, + AXTextArea + }; + + public enum SubRoles + { + AXCloseButton, + }; + + public struct Range + { + public int Location { get; set; } + public int Length { get; set; } + } + +#if MAC + const string XamarinPrivateAtkCocoaNSAccessibilityKey = "xamarin-private-atkcocoa-nsaccessibility"; + internal static INSAccessibility GetNSAccessibilityElement (Atk.Object o) + { + IntPtr handle = GtkWorkarounds.GetData (o, XamarinPrivateAtkCocoaNSAccessibilityKey); + + // The object returned could either be an NSAccessibilityElement or it might be an NSObject that implements INSAccessibility + return Runtime.GetNSObject<NSObject> (handle, false) as INSAccessibility; + } + + internal static void SetNSAccessibilityElement (Atk.Object o, INativeObject native) + { + GtkWorkarounds.SetData (o, XamarinPrivateAtkCocoaNSAccessibilityKey, native.Handle); + } +#endif + +#if MAC + internal static void DumpAccessibilityTree (NSObject obj = null, int indentLevel = 0) + { + if (obj == null) { + obj = NSApplication.SharedApplication; + } + + string desc = obj.Description; + desc = desc.PadLeft (desc.Length + indentLevel, ' '); + Console.WriteLine ($"{desc}"); + + if (!obj.RespondsToSelector (new Selector ("accessibilityChildren"))) { + string notAccessible = "Not accessible"; + Console.WriteLine ($"{notAccessible.PadLeft (notAccessible.Length + indentLevel + 2, ' ')}"); + return; + } + + NSArray children = (NSArray)obj.PerformSelector (new Selector ("accessibilityChildren")); + if (children == null) { + return; + } + + for (nuint i = 0; i < children.Count; i++) { + DumpAccessibilityTree (children.GetItem<NSObject> (i), indentLevel + 2); + } + } +#endif + public static void SetAccessibilityLabel (this Atk.Object o, string label) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityLabel = label; +#endif + } + + public static void SetAccessibilityShouldIgnore (this Atk.Object o, bool ignore) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityElement = !ignore; +#endif + } + + public static void SetAccessibilityTitle (this Atk.Object o, string title) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityTitle = title; +#endif + } + + public static void SetAccessibilityDocument (this Atk.Object o, string documentUrl) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityDocument = documentUrl; +#endif + } + + public static void SetAccessibilityFilename (this Atk.Object o, string filename) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityFilename = filename; +#endif + } + + public static void SetAccessibilityIsMainWindow (this Atk.Object o, bool isMainWindow) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityMain = isMainWindow; +#endif + } + + public static void SetAccessibilityMainWindow (this Atk.Object o, Atk.Object mainWindow) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + var windowAccessible = GetNSAccessibilityElement (mainWindow); + if (windowAccessible == null) { + return; + } + + nsa.AccessibilityMainWindow = (NSObject)windowAccessible; +#endif + } + + public static void SetAccessibilityValue (this Atk.Object o, string stringValue) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityValue = new NSString (stringValue); +#endif + } + + public static void SetAccessibilityURL (this Atk.Object o, string url) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityUrl = new NSUrl (url); +#endif + } + + public static void SetAccessibilityRole (this Atk.Object o, string role, string description = null) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityRole = role; + + if (!string.IsNullOrEmpty (description)) { + nsa.AccessibilityRoleDescription = description; + } +#endif + } + + public static void SetAccessibilityRole (this Atk.Object o, Roles role, string description = null) + { + o.SetAccessibilityRole (role.ToString (), description); + } + + public static void SetAccessibilitySubRole (this Atk.Object o, string subrole) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilitySubrole = subrole; +#endif + } + + public static void SetAccessibilityTitleUIElement (this Atk.Object o, Atk.Object title) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + var titleNsa = GetNSAccessibilityElement (title); + + if (nsa == null || titleNsa == null) { + return; + } + + nsa.AccessibilityTitleUIElement = (NSObject)titleNsa; +#endif + } + + public static void SetAccessibilityAlternateUIVisible (this Atk.Object o, bool visible) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityAlternateUIVisible = visible; +#endif + } + + public static void SetAccessibilityOrientation (this Atk.Object o, Gtk.Orientation orientation) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityOrientation = orientation == Gtk.Orientation.Vertical ? NSAccessibilityOrientation.Vertical : NSAccessibilityOrientation.Horizontal; +#endif + + } + + public static void SetAccessibilityTitleFor (this Atk.Object o, params Atk.Object [] objects) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + NSObject [] titleElements = new NSObject [objects.Length]; + int idx = 0; + + foreach (var obj in objects) { + var nsao = GetNSAccessibilityElement (obj); + if (nsao == null) { + return; + } + + titleElements [idx] = (NSObject)nsao; + idx++; + } + + nsa.AccessibilityServesAsTitleForUIElements = titleElements; +#endif + } + + public static void SetAccessibilityTabs (this Atk.Object o, AccessibilityElementProxy [] tabs) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + nsa.AccessibilityTabs = tabs; +#endif + } + + public static void SetAccessibilityTabs (this Atk.Object o, Atk.Object [] tabs) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + NSObject [] realTabs = new NSObject [tabs.Length]; + int i = 0; + foreach (var tab in tabs) { + realTabs [i] = (NSObject)GetNSAccessibilityElement (tab); + i++; + } + + nsa.AccessibilityTabs = realTabs; +#endif + } + + public static void AccessibilityAddElementToTitle (this Atk.Object title, Atk.Object o) + { +#if MAC + var titleNsa = GetNSAccessibilityElement (title); + var nsa = GetNSAccessibilityElement (o); + + if (nsa == null || titleNsa == null) { + return; + } + + NSObject [] oldElements = titleNsa.AccessibilityServesAsTitleForUIElements; + int length = oldElements != null ? oldElements.Length : 0; + + if (oldElements != null && oldElements.IndexOf ((NSObject)nsa) != -1) { + return; + } + + NSObject [] titleElements = new NSObject [length + 1]; + if (oldElements != null) { + oldElements.CopyTo (titleElements, 0); + } + titleElements [length] = (NSObject)nsa; +#endif + } + + public static void AccessibilityRemoveElementFromTitle (this Atk.Object title, Atk.Object o) + { +#if MAC + var titleNsa = GetNSAccessibilityElement (title); + var nsa = GetNSAccessibilityElement (o); + + if (nsa == null || titleNsa == null) { + return; + } + + if (titleNsa.AccessibilityServesAsTitleForUIElements == null) { + return; + } + + List<NSObject> oldElements = new List<NSObject> (titleNsa.AccessibilityServesAsTitleForUIElements); + oldElements.Remove ((NSObject)nsa); + + titleNsa.AccessibilityServesAsTitleForUIElements = oldElements.ToArray (); +#endif + } + + public static void AccessibilityReplaceAccessibilityElements (this Atk.Object parent, AccessibilityElementProxy [] children) + { +#if MAC + var nsa = GetNSAccessibilityElement (parent); + + if (nsa == null) { + return; + } + + nsa.AccessibilityChildren = children; +#endif + } + + public static void SetAccessibilityColumns (this Atk.Object parent, AccessibilityElementProxy [] columns) + { +#if MAC + var nsa = GetNSAccessibilityElement (parent); + + if (nsa == null) { + return; + } + + nsa.AccessibilityColumns = columns; +#endif + } + + public static void SetAccessibilityRows (this Atk.Object parent, AccessibilityElementProxy [] rows) + { +#if MAC + var nsa = GetNSAccessibilityElement (parent); + + if (nsa == null) { + return; + } + + nsa.AccessibilityRows = rows; +#endif + } + + public static void SetActionDelegate (this Atk.Object o, ActionDelegate ad) + { + ad.Owner = o; + } + + public static void AddAccessibleElement (this Atk.Object o, AccessibilityElementProxy child) + { +#if MAC + var nsa = GetNSAccessibilityElement (o) as NSAccessibilityElement; + if (nsa == null) { + return; + } + + nsa.AccessibilityAddChildElement (child); +#endif + } + + public static void RemoveAccessibleElement (this Atk.Object o, AccessibilityElementProxy child) + { +#if MAC + var nsa = GetNSAccessibilityElement (o); + if (nsa == null) { + return; + } + + var children = nsa.AccessibilityChildren; + + if (children == null || children.Length == 0) { + return; + } + + var idx = children.IndexOf (child); + if (idx == -1) { + return; + } + + var newChildren = new NSObject [children.Length - 1]; + + for (int i = 0, j = 0; i < children.Length; i++) { + if (i == idx) { + continue; + } + + newChildren [j] = children [i]; + j++; + } + + nsa.AccessibilityChildren = newChildren; +#endif + } + } + + public class ActionDelegate + { + public AtkCocoa.Actions [] Actions { get; set; } + + Atk.Object owner; + internal Atk.Object Owner { + set { + owner = value; + + if (owner.GetType () == typeof (Atk.NoOpObject)) { + return; + } + + var signal = GLib.Signal.Lookup (owner, "request-actions", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (RequestActionsHandler)); + + signal = GLib.Signal.Lookup (owner, "perform-cancel", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformCancelHandler)); + signal = GLib.Signal.Lookup (owner, "perform-confirm", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformConfirmHandler)); + signal = GLib.Signal.Lookup (owner, "perform-decrement", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformDecrementHandler)); + signal = GLib.Signal.Lookup (owner, "perform-delete", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformDeleteHandler)); + signal = GLib.Signal.Lookup (owner, "perform-increment", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformIncrementHandler)); + signal = GLib.Signal.Lookup (owner, "perform-pick", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformPickHandler)); + signal = GLib.Signal.Lookup (owner, "perform-press", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformPressHandler)); + signal = GLib.Signal.Lookup (owner, "perform-raise", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformRaiseHandler)); + signal = GLib.Signal.Lookup (owner, "perform-show-alternate-ui", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformShowAlternateUIHandler)); + signal = GLib.Signal.Lookup (owner, "perform-show-default-ui", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformShowDefaultUIHandler)); + signal = GLib.Signal.Lookup (owner, "perform-show-menu", typeof (GLib.SignalArgs)); + signal.AddDelegate (new EventHandler<GLib.SignalArgs> (PerformShowMenuHandler)); + } + } + + void RequestActionsHandler (object sender, GLib.SignalArgs args) + { + // +1 so we can add a NULL to terminate the array + int actionCount = Actions.Length + 1; + IntPtr intPtr = Marshal.AllocHGlobal (actionCount * Marshal.SizeOf<IntPtr> ()); + IntPtr [] actions = new IntPtr [actionCount]; + + int i = 0; + foreach (var action in Actions) { + actions [i] = Marshal.StringToHGlobalAnsi (action.ToString ()); + i++; + } + + // Terminator + actions [i] = IntPtr.Zero; + + Marshal.Copy (actions, 0, intPtr, actionCount); + + args.RetVal = intPtr; + } + + void PerformCancelHandler (object sender, GLib.SignalArgs args) + { + PerformCancel?.Invoke (this, args); + } + + void PerformConfirmHandler (object sender, GLib.SignalArgs args) + { + PerformConfirm?.Invoke (this, args); + } + + void PerformDecrementHandler (object sender, GLib.SignalArgs args) + { + PerformDecrement?.Invoke (this, args); + } + + void PerformDeleteHandler (object sender, GLib.SignalArgs args) + { + PerformDelete?.Invoke (this, args); + } + + void PerformIncrementHandler (object sender, GLib.SignalArgs args) + { + PerformIncrement?.Invoke (this, args); + } + + void PerformPickHandler (object sender, GLib.SignalArgs args) + { + PerformPick?.Invoke (this, args); + } + + void PerformPressHandler (object sender, GLib.SignalArgs args) + { + PerformPress?.Invoke (this, args); + } + + void PerformRaiseHandler (object sender, GLib.SignalArgs args) + { + PerformRaise?.Invoke (this, args); + } + + void PerformShowAlternateUIHandler (object sender, GLib.SignalArgs args) + { + PerformShowAlternateUI?.Invoke (this, args); + } + + void PerformShowDefaultUIHandler (object sender, GLib.SignalArgs args) + { + PerformShowDefaultUI?.Invoke (this, args); + } + + void PerformShowMenuHandler (object sender, GLib.SignalArgs args) + { + PerformShowMenu?.Invoke (this, args); + } + + public event EventHandler PerformCancel; + public event EventHandler PerformConfirm; + public event EventHandler PerformDecrement; + public event EventHandler PerformDelete; + public event EventHandler PerformIncrement; + public event EventHandler PerformPick; + public event EventHandler PerformPress; + public event EventHandler PerformRaise; + public event EventHandler PerformShowAlternateUI; + public event EventHandler PerformShowDefaultUI; + public event EventHandler PerformShowMenu; + } + + // On anything other than Mac this is just a dummy class to prevent needing to have #ifdefs all over the main code + public interface IAccessibilityElementProxy + { + event EventHandler PerformCancel; + event EventHandler PerformConfirm; + event EventHandler PerformDecrement; + event EventHandler PerformDelete; + event EventHandler PerformIncrement; + event EventHandler PerformPick; + event EventHandler PerformPress; + event EventHandler PerformRaise; + event EventHandler PerformShowAlternateUI; + event EventHandler PerformShowDefaultUI; + event EventHandler PerformShowPopupMenu; + + void SetRealParent (Gtk.Widget realParent); + void SetFrameInRealParent (Gdk.Rectangle frame); + void AddAccessibleChild (IAccessibilityElementProxy child); + void SetAccessibilityRole (string role, string description = null); + void SetAccessibilityRole (AtkCocoa.Roles role, string description = null); + void SetAccessibilityValue (string value); + void SetAccessibilityTitle (string title); + void SetAccessibilityLabel (string label); + void SetAccessibilityIdentifier (string identifier); + void SetAccessibilityHelp (string help); + void SetFrameInParent (Gdk.Rectangle rect); + } + + public interface IAccessibilityNavigableStaticText + { + int NumberOfCharacters { get; } + int InsertionPointLineNumber { get; } + string Value { get; } + + // Returns frame in Gtk.Widget parent space. + Gdk.Rectangle GetFrameForRange (AtkCocoa.Range range); + int GetLineForIndex (int index); + AtkCocoa.Range GetRangeForLine (int line); + string GetStringForRange (AtkCocoa.Range range); + AtkCocoa.Range GetRangeForIndex (int index); + AtkCocoa.Range GetStyleRangeForIndex (int index); + AtkCocoa.Range GetRangeForPosition (Gdk.Point position); + } + + /* + public abstract class AtkCellRendererProxy : Atk.Object + { + public AccessibilityElementProxy Accessible { get; private set; } + protected AtkCellRendererProxy () + { + Accessible = new AccessibilityElementProxy (); + + // Set the element as secret data on the Atk.Object so AtkCocoa can do something with it + AtkCocoaHelper.SetNSAccessibilityElement (this, Accessible); + } + } +*/ +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs new file mode 100644 index 0000000000..57712c1818 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs @@ -0,0 +1,505 @@ +// +// AtkCocoaHelperMac.cs +// +// Author: +// iain <iaholmes@microsoft.com> +// +// Copyright (c) 2017 Microsoft Corp +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if MAC + +using System; +using System.Runtime.InteropServices; + +using AppKit; +using CoreGraphics; +using Foundation; +using Gdk; +using ObjCRuntime; + +namespace MonoDevelop.Components.AtkCocoaHelper +{ + public class AccessibilityElementProxy : NSAccessibilityElement, INSAccessibility, IAccessibilityElementProxy + { + public event EventHandler PerformCancel; + public event EventHandler PerformConfirm; + public event EventHandler PerformDecrement; + public event EventHandler PerformDelete; + public event EventHandler PerformIncrement; + public event EventHandler PerformPick; + public event EventHandler PerformPress; + public event EventHandler PerformRaise; + public event EventHandler PerformShowAlternateUI; + public event EventHandler PerformShowDefaultUI; + public event EventHandler PerformShowPopupMenu; + + protected Gtk.Widget parent; + INSAccessibility parentElement; + Rectangle realFrame; + + // The real parent is the Widget that ultimately this object will belong to + // It is used to convert the frame + public void SetRealParent (Gtk.Widget realParent) + { + parent = realParent; + parentElement = AtkCocoa.GetNSAccessibilityElement (parent.Accessible); + } + + // The frame inside the GtkWidget parent, in Gtk coordinate space + public void SetFrameInRealParent (Rectangle frame) + { + realFrame = frame; + } + + public void AddAccessibleChild (IAccessibilityElementProxy child) + { + var realChild = child as NSAccessibilityElement; + if (realChild == null) { + throw new Exception ("The child passed into AddAccessibleChild was not an NSAccessibilityElement"); + } + + AccessibilityAddChildElement (realChild); + } + + public void SetAccessibilityRole (string role, string description = null) + { + AccessibilityRole = role; + if (!string.IsNullOrEmpty (description)) { + AccessibilityRoleDescription = description; + } + } + + public void SetAccessibilityRole (AtkCocoa.Roles role, string description = null) + { + SetAccessibilityRole (role.ToString (), description); + } + + public void SetAccessibilityValue (string value) + { + AccessibilityValue = new NSString (value); + } + + public void SetAccessibilityTitle (string title) + { + AccessibilityTitle = title; + } + + public void SetAccessibilityLabel (string label) + { + AccessibilityLabel = label; + } + + public void SetAccessibilityIdentifier (string identifier) + { + AccessibilityIdentifier = identifier; + } + + public void SetAccessibilityHelp (string help) + { + AccessibilityHelp = help; + } + + public void SetFrameInParent (Rectangle rect) + { + AccessibilityFrameInParentSpace = new CGRect (rect.X, rect.Y, rect.Width, rect.Height); + } + + protected void GetCoordsInWindow (Gtk.Widget widget, out int x, out int y) + { + widget.TranslateCoordinates (widget.Toplevel, 0, 0, out x, out y); + } + + protected void GetCoordsInScreen (Gtk.Widget widget, int windowX, int windowY, out int screenX, out int screenY) + { + var gdkWindow = widget.GdkWindow; + + screenX = windowX; + screenY = windowY; + + if (gdkWindow == null) { + return; + } + + CGRect windowRect = new CGRect (windowX, windowY, 0, 0); + + var ptr = gdk_quartz_window_get_nswindow (gdkWindow.Handle); + if (ptr == IntPtr.Zero) { + return; + } + NSWindow nsWin = Runtime.GetNSObject<NSWindow> (ptr); + + var screenRect = nsWin.ConvertRectToScreen (windowRect); + screenX = (int)screenRect.X; + screenY = (int)screenRect.Y; + } + + protected bool ConvertGtkYCoordToCocoa (Gtk.Widget widget, int gtkY, out int cocoaY) + { + var gdkWindow = widget.GdkWindow; + + cocoaY = gtkY; + + if (gdkWindow == null) { + return false; + } + + var ptr = gdk_quartz_window_get_nswindow (gdkWindow.Handle); + if (ptr == IntPtr.Zero) { + return false; + } + NSWindow nsWin = Runtime.GetNSObject<NSWindow> (ptr); + + // Flip the y coords to Cooca origin + nfloat halfWindowHeight = nsWin.ContentView.Frame.Height / 2; + nfloat dy = gtkY - halfWindowHeight; + + cocoaY = (int)(halfWindowHeight - dy); + return true; + } + + [DllImport ("libgtk-quartz-2.0.dylib")] + static extern IntPtr gdk_quartz_window_get_nswindow (IntPtr window); + + [Export ("accessibilityHitTest:")] + public virtual NSObject GetAccessibilityHitTest (CGPoint pointOnScreen) + { + var gdkWindow = parent.GdkWindow; + + if (gdkWindow == null) { + return this; + } + + var ptr = gdk_quartz_window_get_nswindow (gdkWindow.Handle); + if (ptr == IntPtr.Zero) { + return this; + } + NSWindow nsWin = Runtime.GetNSObject<NSWindow> (ptr); + + CGRect screenRect = new CGRect (pointOnScreen.X, pointOnScreen.Y, 1, 1); + CGRect windowRect = nsWin.ConvertRectFromScreen (screenRect); + CGPoint pointInWindow = new CGPoint (windowRect.X, windowRect.Y); + + // Flip the y coords to Gtk origin + nfloat halfWindowHeight = nsWin.ContentView.Frame.Height / 2; + nfloat dy = pointInWindow.Y - halfWindowHeight; + + CGPoint pointInGtkWindow = new CGPoint (pointInWindow.X, halfWindowHeight - dy); + int parentInWindowX, parentInWindowY; + + GetCoordsInWindow (parent, out parentInWindowX, out parentInWindowY); + + if (AccessibilityChildren == null) { + return this; + } + + foreach (var o in AccessibilityChildren) { + var proxy = o as AccessibilityElementProxy; + if (proxy == null) { + throw new Exception ($"Unsupported type {o.GetType ()} inside AccessibilityElementProxy"); + } + + if (proxy.AccessibilityHidden) { + continue; + } + + var frameInRealParent = proxy.realFrame; + + if (frameInRealParent.X + parentInWindowX < pointInGtkWindow.X && + frameInRealParent.X + parentInWindowX + frameInRealParent.Width >= pointInGtkWindow.X && + frameInRealParent.Y + parentInWindowY < pointInGtkWindow.Y && + frameInRealParent.Y + parentInWindowY + frameInRealParent.Height >= pointInGtkWindow.Y) { + return proxy.GetAccessibilityHitTest (pointOnScreen); + } + } + + return this; + } + + [Export ("accessibilityActionNames")] + public string [] Actions { get; set; } + + [Export ("accessibilityPerformAction:")] + public void performAction (string actionName) + { + switch (actionName) { + case "AXCancel": + OnPerformCancel (); + break; + + case "AXConfirm": + OnPerformConfirm (); + break; + + case "AXDecrement": + OnPerformDecrement (); + break; + + case "AXDelete": + OnPerformDelete (); + break; + + case "AXIncrement": + OnPerformIncrement (); + break; + + case "AXPick": + OnPerformPick (); + break; + + case "AXPress": + OnPerformPress (); + break; + + case "AXRaise": + OnPerformRaise (); + break; + + case "AXShowAlternateUI": + OnPerformShowAlternateUI (); + break; + + case "AXShowDefaultUI": + OnPerformShowDefaultUI (); + break; + + case "AXShowMenu": + OnPerformShowPopupMenu (); + break; + + default: + break; + } + } + + public override bool AccessibilityFocused { + get { + return parent.HasFocus; + } + set { + parent.HasFocus = value; + } + } + + protected bool OnPerformCancel () + { + PerformCancel?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformConfirm () + { + PerformConfirm?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformDecrement () + { + PerformDecrement?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformDelete () + { + PerformDelete?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformIncrement () + { + PerformIncrement?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformPick () + { + PerformPick?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformPress () + { + PerformPress?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformRaise () + { + PerformRaise?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformShowAlternateUI () + { + PerformShowAlternateUI?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformShowDefaultUI () + { + PerformShowDefaultUI?.Invoke (this, EventArgs.Empty); + return true; + } + + protected bool OnPerformShowPopupMenu () + { + PerformShowPopupMenu?.Invoke (this, EventArgs.Empty); + return true; + } + } + + public class AccessibilityElementButtonProxy : AccessibilityElementProxy, INSAccessibilityButton + { + public override bool AccessibilityPerformPress () + { + return OnPerformPress (); + } + } + + public abstract class AccessibilityElementNavigableStaticTextProxy : AccessibilityElementProxy, INSAccessibilityNavigableStaticText, IAccessibilityNavigableStaticText + { + string INSAccessibilityStaticText.AccessibilityValue { + get { + return Value; + } + } + + public override nint AccessibilityInsertionPointLineNumber { + get { + return InsertionPointLineNumber; + } + } + + public override nint AccessibilityNumberOfCharacters { + get { + return NumberOfCharacters; + } + } + + public abstract int NumberOfCharacters { get; } + + public abstract int InsertionPointLineNumber { get; } + + public abstract string Value { get; } + + // Returned frame is in screen coordinate space + [Export ("accessibilityFrameForRange:")] + CGRect AccessibilityFrameForRange (NSRange range) + { + var realRange = new AtkCocoa.Range { Location = (int)range.Location, Length = (int)range.Length }; + var frame = GetFrameForRange (realRange); + + int parentX, parentY; + + // Gtk is giving the top left corner of the bounding box, but Cocoa needs the bottom left + int realFrameY = frame.Y + frame.Height; + GetCoordsInWindow (parent, out parentX, out parentY); + int cocoaY; + if (!ConvertGtkYCoordToCocoa (parent, parentY + realFrameY, out cocoaY)) { + Console.WriteLine ("Error converting coordinate"); + } + + int screenX, screenY; + GetCoordsInScreen (parent, parentX + frame.X, cocoaY, out screenX, out screenY); + + return new CGRect (screenX, screenY, frame.Width, frame.Height); + } + + [Export ("accessibilityLineForIndex:")] + nint AccessibilityLineForIndex (nint index) + { + return GetLineForIndex ((int)index); + } + + [Export ("accessibilityRangeForLine:")] + NSRange AccessibilityRangeForLine (nint line) + { + var range = GetRangeForLine ((int)line); + + return new NSRange (range.Location, range.Length); + } + + [Export ("accessibilityStringForRange:")] + string AccessibilityStringForRange (NSRange range) + { + var realRange = new AtkCocoa.Range { Location = (int)range.Location, Length = (int)range.Length }; + return GetStringForRange (realRange); + } + + [Export ("accessibilityRangeForIndex:")] + NSRange AccessibilityRangeForIndex (nint index) + { + var realRange = GetRangeForIndex ((int)index); + return new NSRange (realRange.Location, realRange.Length); + } + + [Export ("accessibilityStyleRangeForIndex:")] + NSRange AccessibililtyStyleRangeForIndex (nint index) + { + var realRange = GetStyleRangeForIndex ((int)index); + return new NSRange (realRange.Location, realRange.Length); + } + + [Export ("accessibilityRangeForPosition:")] + NSRange AccessibilityRangeForPosition (CGPoint position) + { + var point = new Point ((int)position.X, (int)position.Y); + var realRange = GetRangeForPosition (point); + return new NSRange (realRange.Location, realRange.Length); + } + + public virtual Rectangle GetFrameForRange (AtkCocoa.Range range) + { + throw new NotImplementedException (); + } + + public virtual int GetLineForIndex (int index) + { + throw new NotImplementedException (); + } + + public virtual AtkCocoa.Range GetRangeForLine (int line) + { + throw new NotImplementedException (); + } + + public virtual string GetStringForRange (AtkCocoa.Range range) + { + throw new NotImplementedException (); + } + + public virtual AtkCocoa.Range GetRangeForIndex (int index) + { + throw new NotImplementedException (); + } + + public virtual AtkCocoa.Range GetStyleRangeForIndex (int index) + { + throw new NotImplementedException (); + } + + public virtual AtkCocoa.Range GetRangeForPosition (Point position) + { + throw new NotImplementedException (); + } + } +} + +#endif diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs new file mode 100644 index 0000000000..bd5a510828 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs @@ -0,0 +1,103 @@ +// +// AtkCocoaHelperNoOp.cs +// +// Author: +// iain <iaholmes@microsoft.com> +// +// Copyright (c) 2017 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if !MAC + +using System; +using Gdk; +using Gtk; + +namespace MonoDevelop.Components.AtkCocoaHelper +{ + public class AccessibilityElementProxy : IAccessibilityElementProxy + { + public event EventHandler PerformCancel; + public event EventHandler PerformConfirm; + public event EventHandler PerformDecrement; + public event EventHandler PerformDelete; + public event EventHandler PerformIncrement; + public event EventHandler PerformPick; + public event EventHandler PerformPress; + public event EventHandler PerformRaise; + public event EventHandler PerformShowAlternateUI; + public event EventHandler PerformShowDefaultUI; + public event EventHandler PerformShowPopupMenu; + + public void AddAccessibleChild (IAccessibilityElementProxy child) + { + } + + public void SetAccessibilityHelp (string help) + { + } + + public void SetAccessibilityIdentifier (string identifier) + { + } + + public void SetAccessibilityLabel (string label) + { + } + + public void SetAccessibilityRole (AtkCocoaHelper.Roles role, string description = null) + { + } + + public void SetAccessibilityRole (string role, string description = null) + { + } + + public void SetAccessibilityTitle (string title) + { + } + + public void SetAccessibilityValue (string value) + { + } + + public void SetFrameInParent (Rectangle rect) + { + } + + public void SetFrameInRealParent (Rectangle frame) + { + } + + public void SetRealParent (Widget realParent) + { + } + } + + public class AccessibilityElementButtonProxy + { + } + + public abstract class AccessibilityElementNavigableStaticTextProxy : IAccessibilityNavigableStaticText + { + } +} + +#endif |