Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriain holmes <iain@xamarin.com>2017-05-31 16:42:34 +0300
committeriain holmes <iain@xamarin.com>2017-05-31 16:42:34 +0300
commit4eeb2f1cb67315ad871ac198885dac384352bdb6 (patch)
tree4fe389bde046c4678019b192b733e998d0976724 /main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking
parenta4cd3ac67fc56193172464f04553d9a3af836d4c (diff)
[A11y] Make the DockItemTitleTabs keyboard accessible
Implement tabbing through the dock items, showing their focus
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking')
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs237
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs5
2 files changed, 212 insertions, 30 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs
index 1e385e7637..9d94608c51 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs
@@ -25,7 +25,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-using Gtk;
+using Gtk;
using System;
using MonoDevelop.Ide.Gui;
@@ -38,8 +38,8 @@ using MonoDevelop.Ide.Fonts;
namespace MonoDevelop.Components.Docking
{
-
- class DockItemTitleTab: Gtk.EventBox
+
+ class DockItemTitleTab : Gtk.EventBox
{
static Xwt.Drawing.Image dockTabActiveBackImage = Xwt.Drawing.Image.FromResource ("padbar-active.9.png");
static Xwt.Drawing.Image dockTabBackImage = Xwt.Drawing.Image.FromResource ("padbar-inactive.9.png");
@@ -60,6 +60,7 @@ namespace MonoDevelop.Components.Docking
DockItem item;
bool allowPlaceholderDocking;
bool mouseOver;
+ Widget currentFocus = null; // Currently focused child
IDisposable subscribedLeaveEvent;
@@ -72,6 +73,8 @@ namespace MonoDevelop.Components.Docking
static readonly Xwt.WidgetSpacing TabPadding;
static readonly Xwt.WidgetSpacing TabActivePadding;
+ internal event EventHandler<EventArgs> TabPressed;
+
static DockItemTitleTab ()
{
pixClose = Xwt.Drawing.Image.FromResource ("pad-close-9.png");
@@ -95,7 +98,7 @@ namespace MonoDevelop.Components.Docking
tabActiveBackImage9 = dockTabActiveBackImage as Xwt.Drawing.NinePatchImage;
TabActivePadding = tabActiveBackImage9.Padding;
}
-
+
public DockItemTitleTab (DockItem item, DockFrame frame)
{
var actionHandler = new ActionDelegate (this);
@@ -105,6 +108,7 @@ namespace MonoDevelop.Components.Docking
Accessible.SetRole (AtkCocoa.Roles.AXGroup, "pad header");
Accessible.SetSubRole ("XAPadHeader");
+ CanFocus = true;
this.item = item;
this.frame = frame;
this.VisibleWindow = false;
@@ -194,10 +198,10 @@ namespace MonoDevelop.Components.Docking
tabIcon = new ImageView ();
tabIcon.Accessible.SetShouldIgnore (true);
tabIcon.Show ();
- box.PackStart (tabIcon, false, false, 3);
-
- labelWidget = new ExtendedLabel (label);
- // Ignore the label because the title tab already contains its name
+ box.PackStart (tabIcon, false, false, 3);
+
+ labelWidget = new ExtendedLabel (label);
+ // Ignore the label because the title tab already contains its name
labelWidget.Accessible.SetShouldIgnore (true);
labelWidget.UseMarkup = true;
var alignLabel = new Alignment (0.0f, 0.5f, 1, 1);
@@ -210,8 +214,8 @@ namespace MonoDevelop.Components.Docking
btnDock = new ImageButton ();
btnDock.Image = pixAutoHide;
btnDock.TooltipText = GettextCatalog.GetString ("Auto Hide");
- btnDock.CanFocus = false;
- // btnDock.WidthRequest = btnDock.HeightRequest = 17;
+ btnDock.CanFocus = true;
+ // btnDock.WidthRequest = btnDock.HeightRequest = 17;
btnDock.Clicked += OnClickDock;
btnDock.ButtonPressEvent += (o, args) => args.RetVal = true;
btnDock.WidthRequest = btnDock.SizeRequest ().Width;
@@ -220,8 +224,8 @@ namespace MonoDevelop.Components.Docking
btnClose = new ImageButton ();
btnClose.Image = pixClose;
btnClose.TooltipText = GettextCatalog.GetString ("Close");
- btnClose.CanFocus = false;
- // btnClose.WidthRequest = btnClose.HeightRequest = 17;
+ btnClose.CanFocus = true;
+ // btnClose.WidthRequest = btnClose.HeightRequest = 17;
btnClose.WidthRequest = btnDock.SizeRequest ().Width;
btnClose.Clicked += delegate {
item.Visible = false;
@@ -250,8 +254,7 @@ namespace MonoDevelop.Components.Docking
btnClose.Name = string.Format ("btnClose_{0}", labelNoSpaces ?? string.Empty);
realLabel = GettextCatalog.GetString ("Close {0}", label);
realHelp = GettextCatalog.GetString ("Close the {0} pad", label);
- }
- else {
+ } else {
labelWidget.Parent.Hide ();
realLabel = GettextCatalog.GetString ("Close pad");
realHelp = GettextCatalog.GetString ("Close the pad");
@@ -266,7 +269,7 @@ namespace MonoDevelop.Components.Docking
Accessible.SetTitle (label);
Accessible.SetLabel (label);
}
-
+
// Get the required size before setting the ellipsize property, since ellipsized labels
// have a width request of 0
box.ShowAll ();
@@ -320,7 +323,7 @@ namespace MonoDevelop.Components.Docking
public int MinWidth {
get { return minWidth; }
}
-
+
public bool Active {
get {
return active;
@@ -341,7 +344,7 @@ namespace MonoDevelop.Components.Docking
return page;
}
}
-
+
public void UpdateBehavior ()
{
if (btnClose == null)
@@ -349,7 +352,7 @@ namespace MonoDevelop.Components.Docking
btnClose.Visible = (item.Behavior & DockItemBehavior.CantClose) == 0;
btnDock.Visible = (item.Behavior & DockItemBehavior.CantAutoHide) == 0;
-
+
if (active || mouseOver) {
if (btnClose.Image == null)
btnClose.Image = pixClose;
@@ -371,6 +374,13 @@ namespace MonoDevelop.Components.Docking
bool tabPressed, tabActivated;
double pressX, pressY;
+ protected override void OnActivate ()
+ {
+ TabPressed?.Invoke (this, EventArgs.Empty);
+
+ base.OnActivate ();
+ }
+
protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
{
if (evnt.TriggersContextMenu ()) {
@@ -381,6 +391,7 @@ namespace MonoDevelop.Components.Docking
tabPressed = true;
pressX = evnt.X;
pressY = evnt.Y;
+ TabPressed?.Invoke (this, EventArgs.Empty);
} else if (evnt.Type == Gdk.EventType.TwoButtonPress) {
tabActivated = true;
}
@@ -398,8 +409,7 @@ namespace MonoDevelop.Components.Docking
else
item.Status = DockItemStatus.AutoHide;
}
- }
- else if (!evnt.TriggersContextMenu () && evnt.Button == 1) {
+ } else if (!evnt.TriggersContextMenu () && evnt.Button == 1) {
frame.DockInPlaceholder (item);
frame.HidePlaceholder ();
if (GdkWindow != null)
@@ -411,15 +421,25 @@ namespace MonoDevelop.Components.Docking
return base.OnButtonReleaseEvent (evnt);
}
+ protected override bool OnFocusInEvent (Gdk.EventFocus evnt)
+ {
+ mouseOver = true;
+ UpdateBehavior ();
+ return base.OnFocusInEvent (evnt);
+ }
+
+ protected override bool OnFocusOutEvent (Gdk.EventFocus evnt)
+ {
+ if (currentFocus == null) {
+ mouseOver = false;
+ UpdateBehavior ();
+ }
+ return base.OnFocusOutEvent (evnt);
+ }
+
void HandlePress (object sender, EventArgs args)
{
- // FIXME: How to support double click?
- frame.DockInPlaceholder (item);
- frame.HidePlaceholder ();
- if (GdkWindow != null)
- GdkWindow.Cursor = null;
- frame.Toplevel.KeyPressEvent -= HeaderKeyPress;
- frame.Toplevel.KeyReleaseEvent -= HeaderKeyRelease;
+ TabPressed?.Invoke (this, EventArgs.Empty);
}
void HandleShowMenu (object sender, EventArgs args)
@@ -455,6 +475,163 @@ namespace MonoDevelop.Components.Docking
UpdateBehavior ();
}
+ enum FocusWidget
+ {
+ None,
+ Widget,
+ DockButton,
+ CloseButton
+ };
+
+ bool FocusCurrentWidget (DirectionType direction)
+ {
+ if (currentFocus == null) {
+ return false;
+ }
+
+ return currentFocus.ChildFocus (direction);
+ }
+
+ bool MoveFocusToWidget (FocusWidget widget, DirectionType direction)
+ {
+ switch (widget) {
+ case FocusWidget.Widget:
+ GrabFocus ();
+ currentFocus = null;
+ return true;
+
+ case FocusWidget.DockButton:
+ currentFocus = btnDock;
+ return btnDock.ChildFocus (direction);
+
+ case FocusWidget.CloseButton:
+ currentFocus = btnClose;
+ return btnClose.ChildFocus (direction);
+
+ case FocusWidget.None:
+ break;
+ }
+
+ return false;
+ }
+
+ FocusWidget GetNextWidgetToFocus (FocusWidget widget, DirectionType direction) {
+ FocusWidget nextSite;
+
+ switch (widget) {
+ case FocusWidget.CloseButton:
+ switch (direction) {
+ case DirectionType.TabForward:
+ case DirectionType.Right:
+ case DirectionType.Down:
+ return FocusWidget.None;
+
+ case DirectionType.TabBackward:
+ case DirectionType.Left:
+ case DirectionType.Up:
+ if (btnDock.Image != null) {
+ nextSite = FocusWidget.DockButton;
+ } else {
+ nextSite = FocusWidget.Widget;
+ }
+ return nextSite;
+ }
+
+ break;
+
+ case FocusWidget.DockButton:
+ switch (direction) {
+ case DirectionType.TabForward:
+ case DirectionType.Right:
+ case DirectionType.Down:
+ return btnClose.Image == null ? FocusWidget.None : FocusWidget.CloseButton;
+
+ case DirectionType.TabBackward:
+ case DirectionType.Left:
+ case DirectionType.Up:
+ return FocusWidget.Widget;
+ }
+
+ break;
+
+ case FocusWidget.Widget:
+ switch (direction) {
+ case DirectionType.TabForward:
+ case DirectionType.Right:
+ case DirectionType.Down:
+ if (btnDock.Image != null) {
+ nextSite = FocusWidget.DockButton;
+ } else if (btnClose.Image != null) {
+ nextSite = FocusWidget.CloseButton;
+ } else {
+ nextSite = FocusWidget.None;
+ }
+ return nextSite;
+
+ case DirectionType.TabBackward:
+ case DirectionType.Left:
+ case DirectionType.Up:
+ return FocusWidget.None;
+ }
+
+ break;
+ case FocusWidget.None:
+ switch (direction) {
+ case DirectionType.TabForward:
+ case DirectionType.Right:
+ case DirectionType.Down:
+ return FocusWidget.Widget;
+
+ case DirectionType.TabBackward:
+ case DirectionType.Left:
+ case DirectionType.Up:
+ if (btnClose.Image != null) {
+ nextSite = FocusWidget.CloseButton;
+ } else if (btnDock.Image != null) {
+ nextSite = FocusWidget.DockButton;
+ } else {
+ nextSite = FocusWidget.Widget;
+ }
+ return nextSite;
+ }
+
+ break;
+ }
+
+ return FocusWidget.None;
+ }
+
+ protected override bool OnFocused (DirectionType direction)
+ {
+ if (!FocusCurrentWidget (direction)) {
+ FocusWidget focus = FocusWidget.None;
+
+ if (currentFocus == btnClose) {
+ focus = FocusWidget.CloseButton;
+ } else if (currentFocus == btnDock) {
+ focus = FocusWidget.DockButton;
+ } else if (IsFocus) {
+ focus = FocusWidget.Widget;
+ }
+
+ while ((focus = GetNextWidgetToFocus (focus, direction)) != FocusWidget.None) {
+ if (MoveFocusToWidget (focus, direction)) {
+ return true;
+ }
+ }
+
+ // Clean up the icons because OnFocusOutEvent has already been called
+ // so we need the icons to hide again
+ mouseOver = false;
+ UpdateBehavior ();
+
+ currentFocus = null;
+ return false;
+ }
+
+ return true;
+ }
+
[GLib.ConnectBeforeAttribute]
void HeaderKeyPress (object ob, Gtk.KeyPressEventArgs a)
{
@@ -527,6 +704,12 @@ namespace MonoDevelop.Components.Docking
DrawAsBrowser (evnt);
else
DrawNormal (evnt);
+
+ if (HasFocus) {
+ var alloc = labelWidget.Allocation;
+ Gtk.Style.PaintFocus (Style, GdkWindow, State, alloc, this, "label",
+ alloc.X, alloc.Y, alloc.Width, alloc.Height);
+ }
return base.OnExposeEvent (evnt);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs
index 82dc1dd48e..c9e986f21a 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs
@@ -94,7 +94,7 @@ namespace MonoDevelop.Components.Docking
tab.Page.Hide ();
}
- tab.ButtonPressEvent += OnTabPress;
+ tab.TabPressed += OnTabPress;
UpdateAccessibilityTabs ();
}
@@ -198,13 +198,12 @@ namespace MonoDevelop.Components.Docking
box.Remove (w);
}
- void OnTabPress (object s, Gtk.ButtonPressEventArgs args)
+ void OnTabPress (object s, EventArgs args)
{
CurrentTab = Array.IndexOf (box.Children, s);
DockItemTitleTab t = (DockItemTitleTab) s;
DockItem.SetFocus (t.Page);
QueueDraw ();
- args.RetVal = true;
}
protected override void OnSizeAllocated (Gdk.Rectangle allocation)