Bug 10846 - Tab titles not read by JAWS
Summary: Tab titles not read by JAWS
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 2.0   Edit
Hardware: PC Windows 2000
: P1 enhancement with 1 vote (vote)
Target Milestone: ---   Edit
Assignee: Stefan Xenos CLA
QA Contact:
URL:
Whiteboard:
Keywords: accessibility, investigate
: 37276 (view as bug list)
Depends on: 21771 48846
Blocks:
  Show dependency tree
 
Reported: 2002-03-06 08:50 EST by Tod Creasey CLA
Modified: 2004-04-29 15:23 EDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tod Creasey CLA 2002-03-06 08:50:15 EST
At no point when you are tabbing around do you get told what view you are in by 
JAWS
Comment 1 Kevin Haaland CLA 2002-03-07 22:05:21 EST
We may have to resort to a script to detect this case. The view activation is 
custom SWT code. 
Comment 2 Tod Creasey CLA 2002-03-14 13:30:36 EST
Platform limitation. Can be deferred.
Comment 3 Tod Creasey CLA 2002-03-19 13:46:54 EST
Marking as later as it is a platfomr limitation.
Comment 4 Tod Creasey CLA 2002-07-25 09:11:14 EDT
Should investigate what the new accessibility API will allow.
Comment 5 Nick Edgar CLA 2002-07-25 09:17:25 EDT
Agree, this would be valuable.  Perhaps changing the pane to return the view's 
tab name or title as the accessible name.  The title would be better since it 
often has more info (e.g. the tasks and search views).
Comment 6 Tod Creasey CLA 2002-07-25 12:56:10 EDT
This is another case where JAWS will ask for the name of the widget via the 
accessibility interface and then do nothing with it. Adding Bug 21771 as a 
blocking bug.
Comment 7 Matthew King CLA 2003-04-24 13:03:08 EDT
This is the type of information that the user would normally expect JAWS to 
report with insert+tab or insert+t. Making it do that would require two things: 
that the information be available to JAWS script functions via the Eclipse 
window hierarchy and writing a JAWS script to support it. I'm willing to write 
the script.
However, there are several ways this information could be made available to the 
user without any scripting. The best idea I have is to provide functions that 
will put up a view list and a perspective list. It sort of already exists. I 
noticed that if one presses ctrl+f7 followed by ctrl+shift+f7 without releasing 
the ctrl key then a list of views takes focus. This is really cool and useful 
but very backdoor. If I could just press something like alt+f7 to pop up the 
list then I could find out the current view with JAWS because its title would 
have focus.
Actually, if one was to be more "consistent" with Windows-like conventions, a 
better list of key assignments would be:
ctrl+f7 = view list
alt+f7 = next view
alt+shift+f7 = previous view
... similar for perspectives and editors.
This is analogous to the Windows alt+tab and alt+shift+tab. Windows is missing 
an assignment to control+tab. We had one in OS/2 ... control+escape pulled up a 
list of open windows.
Comment 8 Carolyn MacLeod CLA 2004-02-11 16:57:54 EST
Needs to be retested in "New Look" views.
Comment 9 Tod Creasey CLA 2004-03-05 10:37:13 EST
Assigning to me for retest
Comment 10 Tod Creasey CLA 2004-03-05 10:43:23 EST
Now the view titles are tabs but JAWS does not read them. Window Eyes does as 
does MSAA.

Moving back to SWT for investigation.
Comment 11 Tod Creasey CLA 2004-03-05 10:47:40 EST
See bug 48846 for more details
Comment 12 Matthew King CLA 2004-03-05 13:47:48 EST
Try reclassing the control as MSAA in JAWS.
1. Put focus on a tab
2. press insert+7
3. Choose MS Active accessibility from the list
4. Press add class and OK
Now see if it works. If not, you can undo the  change.
If it does work, then Frank could take up issue of requiring the 
reclassification with FS. The goal is to avoid requiring all kinds of that type 
of customization to JAWS.
Comment 13 Nick Edgar CLA 2004-03-07 21:19:13 EST
Note that the problem here may be due to the parenting relationships used in 
the Workbench.  View panes are parented under the main content area of the 
window, not the CTabFolder.  They're just positioned under the CTabFolder.  
This allows us to use the same ViewPanes without reparenting when switching 
between perspectives, fast views, etc.

Should compare with a standalone SWT example.
Comment 14 Carolyn MacLeod CLA 2004-04-13 13:43:28 EDT
This comment is similar to comment #7, but maybe a bit more info. I just added 
this comment to bug 37276, and then decided to paste it here also. Bug 37276 
may be a dup, but I will let the UI team decide that.
-----

FYI, this is a bit of a hack, but one way to have JAWS and Window-Eyes read 
the current View name or the current Editor name is to use the eclipse 
CTRL+F6/F7 hotkeys. These allow navigating to editors/views in a similar 
manner to the way Windows' & GTK's ALT+TAB and ALT+SHIFT+TAB let you cycle 
through (and navigate to) application windows.

Here are the eclipse hotkeys in detail:

CTRL+F6 - open a list of editors, with the 'next' one selected. Release the 
keys to navigate to the selected editor. Type CTRL+F6, and release, twice in a 
row to get the screen reader to read the name of the current editor.

CTRL+SHIFT+F6 - open a list of editors, with the 'previous' one selected. 
Release the keys to navigate to the selected editor. Type CTRL+SHIFT+F6, and 
release, twice in a row to get the screen reader to read the name of the 
current editor.

CTRL+F7 - open a list of views, with the 'next' one selected. Release the keys 
to navigate to the selected view. Type CTRL+F7, and release, twice in a row to 
get the screen reader to read the name of the current view.

CTRL+SHIFT+F7 - open a list of views, with the 'previous' one selected. 
Release the keys to navigate to the selected view. Type CTRL+SHIFT+F7, and 
release, twice in a row to get the screen reader to read the name of the 
current view.
Comment 15 Carolyn MacLeod CLA 2004-04-13 15:53:23 EDT
To address Nick's comment 13, I have a standalone SWT CTabFolder example. When 
comparing eclipse view and editor CTabFolders with SWT standalone CTabFolders, 
you will note that there are quite a few differences in keyboard access and 
screen reader accessibility capabilities.

1) User can change the pages of a standalone CTabFolder using ctrl+PgUp and 
ctrl+PgDown. I don't know who in eclipse is eating ctrl+PgUp and ctrl+PgDown, 
but it is not getting to the view or editor CTabFolders.

2) User can always use ctrl+Tab or shift+Tab to give focus to a standalone 
CTabFolder's tab label, and from there, user can always use left/right arrow 
keys to flip through the tabs. In eclipse views and editors, you can only 
sometimes use ctrl+Tab or shift+Tab to give focus to the tabs (for example, 
the editors always eat them), and even then, the left/right arrow keys are 
almost always eaten (they seem to be eaten by the child of the tab?). So 
sometimes, but not always, the user can type shift+Tab, arrow, shift+Tab, 
arrow, etc to cycle through the tabs. But in the standalone, there is no 
eating of any keys at all, so all are available for traversal.

3) Because of the ease of switching focus to the tab label of a standalone 
CTabFolder, JAWS has no trouble reading CTabFolder tab labels in the 
standalone example. The user can at any time switch focus to any tab label, 
which will then be read. It can then be reread with INS+Tab, or if the shell 
loses and regains focus, the tab label is reread. As switching focus to a tab 
label in eclipse is not reliable, it is not the recommended way to get JAWS or 
any screen reader to read the tab labels.

Does the UI team have any time to investigate who is eating all of these 
traversal keys (and why) in eclipse?
Comment 16 Tod Creasey CLA 2004-04-13 16:06:20 EDT
*** Bug 37276 has been marked as a duplicate of this bug. ***
Comment 17 Nick Edgar CLA 2004-04-13 16:29:08 EDT
There are three reasons for the problems above.

1.  Views and editors are not children of the tab folder, so Ctrl+Page_Up etc
are not propogated by the OS to the folder when the view has focus.  Maybe we
can work around this with explicit traversal handling in PartPane or the
presentations mechanism.  This would also allow us to cycle between editors
using Ctrl+Tab.

2. When a tab is selected, the corresponding part is activated, giving it focus. 
This is a bug.  This should only be done if the tab is clicked on, not when
using keyboard.  Can this be accomplished with the NO_FOCUS style?  Otherwise
we'll need to do it ourselves via mouse listeners (yuck).

3. Text editors eat Tab and Shift+Tab.  Use Ctrl+Tab and Ctrl+Shift+Tab instead.
Comment 18 Carolyn MacLeod CLA 2004-04-13 17:38:03 EDT
1a. Worth trying explicit traversal handling for Ctrl+PgUp/Ctrl+PgDown. The 
fact that there is no keyboard way to reliably switch pages in a view or 
editor tab folder is a bit strange, even though there is a hotkey way to 
globally cycle to another view/editor from the current page.

1b. Not sure what you mean by using Ctrl+Tab to cycle through editors - would 
be nice (and consistent with everybody else, and with what JAWS thinks), but 
Ctrl+Tab is already stolen for tab traversal - no? I'd love to make both 
TabFolder and CTabFolder use Ctrl+Tab as the default key for switching pages, 
but then people couldn't traverse forwards out of multi-line texts in tab 
folders, including in any dialogs that have tab folders. Not sure how 
important that is... they can use Shift+Tab (in a dialog) or Ctrl+Shift+Tab 
(in our editors)... but I thought it was a pretty big issue. I'd be happy to 
go there if you think it is important. I was just complaining about it to SN 
the other day because when JAWS is in a tab folder it says, "To switch pages 
press control tab". He said "Let's just fix it", but for something like that, 
you need a lot of people to agree...

2. Not sure how you would use NO_FOCUS style to change activation based on 
whether the tab was selected by mouse or by keyboard? Also, NO_FOCUS is 
a 'hint' - not sure which platforms it will work on. I will try a few things 
in my standalone example, and also check with SN, to see if there's anything 
else interesting either of us can think of.

3. Oops - sorry that I didn't think to try Ctrl+Shift+Tab from inside an 
editor, and I didn't see where focus went with Ctrl+Tab, so I thought it went 
nowhere. I just needed to type a few more Ctrl+Tabs to see that focus had 
indeed traversed out of the editor.
Comment 19 Carolyn MacLeod CLA 2004-04-27 16:45:20 EDT
Note: As per discussion with Tod, reassigned to Stefan to fix 'problem 1' as 
described below. Note that this is P1 for accessibility for 3.0.

Details:

I have addressed problems 1 and 2 from comment #15 (same in comments 17 & 18).
Doing this has significantly improved the keyboard accessibility of the Views 
and Editors tab folders, which really helps screen reader accessibility. I may 
be able to do more with the screen readers by working with the folks at FS, 
but I think we can now say that the eclipse tab folders are accessible. Tod to 
decide after both fixes are in.

Problem 2 (arrow keys were not useful) has been fixed in CTabFolder and 
released to HEAD - it will be in today's integration build.

Problem 1 (Ctrl+PgUp/Ctrl+PgDown not useful) must be fixed by the UI team.  I 
am including a snippet to show how. The relevant lines of code from the 
snippet are:

tabContents.addTraverseListener(new TraverseListener() {
	public void keyTraversed(TraverseEvent e) {
		switch (e.detail) {
			case SWT.TRAVERSE_PAGE_NEXT:
			case SWT.TRAVERSE_PAGE_PREVIOUS:
				tabFolder.traverse(e.detail);
				e.detail = SWT.TRAVERSE_NONE;
				e.doit = true;
		}
	}
});

You just have to figure out which controls are "tabContents" and "tabFolder".
The best place to hook the traverse listener is on the fake child(ren) (i.e. 
actually sibling) of the CTabFolder(s) so that they can forward the "page 
next/previous" traversal to the CTabFolder. The line "e.detail = 
SWT.TRAVERSE_NONE" tells the fake child not to do any traversal itself, and 
the line "e.doit = true" says that the traversal has been handled by someone, 
so don't pass it to anybody else.

Here is the snippet, in full. It emulates eclipse's "tabfolder has siblings 
that act like children" story. While it can be said that this story may have 
other problems in future where additional traversal types should be forwarded, 
for now I recommend just forwarding this one traversal (page next/previous). I 
also recommend checking "instanceof CTabFolder" before forwarding (i.e. be 
very specific). Forwarding other traversals should be done carefully on an as-
needed basis.

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.custom.*;

public class EclipseCTabFolderAccessibilityTest {
	static Display display;
	static Shell shell;
	static CTabFolder tabFolder;
	static boolean sibling = true; // whether tab content is child or 
sibling of tab folder
	static Point maxSize;
	static Composite selectedContents;
	
	public static void main(String[] args) {
		display = new Display();
		shell = new Shell(display);
		if (!sibling) shell.setLayout(new GridLayout());
		shell.setText("Eclipse CTabFolder Accessibility Test");
		maxSize = new Point(0, 0);
		tabFolder = new CTabFolder(shell, SWT.BORDER);
		for (int tabNumber = 0; tabNumber < 5; tabNumber++) {
			CTabItem item = new CTabItem(tabFolder, SWT.NULL);
			item.setText("CTab &" + tabNumber);
			Composite tabContents = new Composite(sibling ? 
(Composite) shell : (Composite) tabFolder, SWT.NONE);
			tabContents.setLayout(new GridLayout());
			createTabContents(tabContents, tabNumber);
			if (sibling) {
				tabContents.addTraverseListener(new 
TraverseListener() {
					public void keyTraversed(TraverseEvent 
e) {
						switch (e.detail) {
							case 
SWT.TRAVERSE_PAGE_NEXT:
							case 
SWT.TRAVERSE_PAGE_PREVIOUS:
							
	tabFolder.traverse(e.detail);
								e.detail = 
SWT.TRAVERSE_NONE;
								e.doit = true;
						}
					}
				});
				tabContents.pack();
				Point size = tabContents.getSize();
				if (size.x > maxSize.x || size.y > maxSize.y) 
maxSize = size;
				tabContents.setVisible(false);
				item.setData(tabContents);
			} else {
				item.setControl(tabContents);
			}
		}
		tabFolder.moveBelow(null);
		if (sibling) {
			tabFolder.setSize(tabFolder.computeSize(maxSize.x, 
maxSize.y));
			shell.addControlListener(new ControlAdapter() {
				public void controlResized(ControlEvent e) {
					tabFolder.setBounds(shell.getClientArea
());
					CTabItem item = tabFolder.getSelection
();
					if (item == null) return;
					setItemBounds(item);
				}
			});
			tabFolder.addSelectionListener(new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					showItem((CTabItem) e.item);
				}
			});
		} else {
			new Button(shell, SWT.PUSH).setText("Hello");
		}
		shell.pack();
		shell.open();
		tabFolder.setSelection(0);
		if (sibling) showItem(tabFolder.getItem(0));
		
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) display.sleep();
		}
	}
	
	static void setItemBounds(CTabItem item) {
		Composite tabContents = (Composite) item.getData();
		tabContents.setBounds(tabFolder.getClientArea());
	}

	static void showItem(CTabItem item) {
		Composite tabContents = (Composite) item.getData();
		tabContents.setBounds(tabFolder.getClientArea());
		if (selectedContents != null) selectedContents.setVisible
(false);
		tabContents.setVisible(true);
		selectedContents = tabContents;
		tabContents.setFocus();
	}

	static void createTabContents(Composite tabContents, int tabNumber) {
		Label itemLabel = new Label(tabContents, SWT.NONE);
		itemLabel.setText("Label for CTabItem " + tabNumber + " is a 
child of a composite");
		switch (tabNumber) {
			case 0:
				Text itemText = new Text(tabContents, 
SWT.MULTI | SWT.BORDER);
				itemText.setLayoutData(new GridData
(GridData.FILL_BOTH));
				itemText.setText("\nText for CTabItem " + 
tabNumber + "\n\n\n");
				break;
			case 1:
				Button itemCheck = new Button(tabContents, 
SWT.CHECK);
				itemCheck.setLayoutData(new GridData
(GridData.FILL_HORIZONTAL));
				itemCheck.setText("Checkbox for CTabItem " + 
tabNumber);
				break;
			case 2:
				Tree itemTree = new Tree(tabContents, 
SWT.SINGLE);
				itemTree.setLayoutData(new GridData
(GridData.FILL_BOTH));
				for (int i = 0; i < 4; i++) {
					TreeItem treeItem = new TreeItem 
(itemTree, SWT.NULL);
					treeItem.setText ("TreeItem " + i + " 
for CTabItem " + tabNumber);
					for (int j = 0; j < 3; j++) {
						new TreeItem(treeItem, 
SWT.NONE).setText("TreeItem " + i + j + " for CTabItem " + tabNumber);
					}
				}
				break;
			case 3:
				Table itemTable = new Table(tabContents, 
SWT.SINGLE);
				itemTable.setLayoutData(new GridData
(GridData.FILL_BOTH));
				itemTable.setHeaderVisible(true);
				itemTable.setLinesVisible(true);
				for (int col = 0; col < 2; col++) {
					TableColumn column = new TableColumn
(itemTable, SWT.NONE);
					column.setText("Column " + col);
				}
				for (int i = 0; i < 4; i++) {
					TableItem tableItem = new TableItem 
(itemTable, SWT.NULL);
					tableItem.setText (new String [] 
{"TableItem " + i, "for CTabItem " + tabNumber});
				}
				for (int col = 0; col < 2; col++) {
					itemTable.getColumn(col).pack();
				}
				break;
			case 4:
				ToolBar itemToolBar = new ToolBar(tabContents, 
SWT.FLAT);
				itemToolBar.setLayoutData(new GridData
(GridData.FILL_HORIZONTAL));
		        String[] toolBarFileNames = new String [] 
{"save", "saveas", "printer", "debug", "run", "search", "opentype"};
				for (int tool = 0; tool < 
toolBarFileNames.length; tool++) {
					String fileName = toolBarFileNames
[tool];
		           	ToolItem item = new ToolItem(itemToolBar, 
SWT.PUSH);
		            item.setImage(createToolBarIcon(display, 
fileName));
		            item.setToolTipText(fileName + " ToolItem for 
CTabItem " + tabNumber);
				}
				break;
		}
	}

	static Image createToolBarIcon(Display display, String fileName) {
		try {
			ImageData source = new ImageData
(AccessibleToolBarTest.class.getResourceAsStream(fileName + ".gif"));
			ImageData mask = source.getTransparencyMask();
			return new Image(display, source, mask);
		} catch (Exception e) {
		}
		return null;
	}
}
Comment 20 Tod Creasey CLA 2004-04-28 15:10:42 EDT
The P1 part of this problem report (the regression with arrow keys in the tabs 
is fixed).

The part assigned to Stefan is an enhancement but it would be new for 3.0. It 
would likely be better to create a new Bug for this enhancement and close this 
as fixed.

I have verified this in 20040327
Comment 21 Carolyn MacLeod CLA 2004-04-29 15:23:50 EDT
Closing this bug. The "enhancement" has been spun off into new bug 60489.