Repository: nvaccess/nvda

Issue:
Status bar handling: allow app modules to define their own routines to fetch status bar and use it in status bar command in global commands
**Reported by nvdakor on 2014-11-21 19:15**
Hi,
Some developers at NVDA development list proposes the following:
As there are situations where one may wish to emulate a status bar and/or read a particular status bar when an app has more than one status bar, let NVDA query the app module for a routine for fetching the desired status bar object before resorting to using status bar routine defined in API module. If this is implemented, it would open new possibilities for app modules to specify which status is which or provde a "fake" status bar. Some of the possibilities are:
- If an app has two or more status bar objects, let the app choose which one to indicate as a status bar.
- If an app doesn't have a status bar yet if the user wishes to hear status information, then allow NvDA to announce the "fake" status bar i.e. the status information.
  This second scenario would allow an app module like that of Vim to let the user hear important status information using a familiar command.
  Implementation strategies and current situation:
- If one wishes to modify how the status bar is returned, one needs to modify NVDA core. This isn't ideal for add-on writers.
- For some apps, NVDA returns "no status bar found" when there is info displayed somewhere (Vim is a good example).
- One way to get around this issue is to introduce a routine in base app module that can be overwritten in subclasses to return the desired status object, or if one desires, a routine that simulates a status bar. Then in global commands, have NvDA check to see if the app module for the object has this new routine, and if not, resort to using the core routine for fetching status bar objects.
- For consistency, the app module routine should set navigator object to the desired status object. A major issue is if we have no status bar objects and if the app module simulates a status bar. Thus the base app module should take care of setting navigator object to the desired status object, with subclasses doing their work and optionally would call the super method if need to.
  Thanks.



Discussion:
Comment 1 by jteh on 2014-11-22 04:52
Doing this at the app module level is probably not ideal, as there could be multiple top level windows in an application. It's probably better to do it on the foreground NVDAObject.

Out of interest, does Vim actually have a status bar object? If so and it's at the bottom of the screen, you just need to give it ROLE_STATUSBAR. If not, where does the status bar information come from?

Comment 2 by lpintes (in reply to comment 1) on 2014-11-22 08:14
Replying to [jteh](comment:1):

> Out of interest, does Vim actually have a status bar object? If so and it's at the bottom of the screen, you just need to give it ROLE_STATUSBAR. If not, where does the status bar information come from?

The Gvim has a window which tries to simulate the console. So there is no status bar object. I need to parse the displayText property and extract the last line.
An interesting thing about Gvim's displayText is that there are no line separator, perhaps due to its "console nature", so I need to split the whole text to chunks. But this is slightly OT in this context.

Comment 3 by jteh on 2014-12-01 04:46
NVDA's report status line command should already be trying the bottom line of screen review if getting a status bar object fails. Do you have any idea why this doesn't work in this case? What happens if you go into screen review and then read the bottom line?

Comment 4 by lpintes (in reply to comment 3) on 2014-12-01 08:12
Hi,
In case of Gvim, displayModel does very bad job indeed. The reportStatusLine script reports "Status bar not found". This is perhaps correct, because there is no status line object.
When you activate the review mode and go directly to the last line, NVDA stucks on something that is reported as empty or blank.
However if you are reviewing line by line, the bottom line can be read.
The Gvim window layout is perhaps a bit weird. There is a window with window class "Vim". This is the window with focus.
Text is drawn to the window with window class "VimTextArea". Then there is a toolbar and in some cases the page control. I don't know if this layout causes problems with displayModel.

Comment 5 by jteh (in reply to comment 4) on 2014-12-01 09:47
Replying to [lpintes](comment:4):

> In case of Gvim, displayModel does very bad job indeed.

It's worth noting that displayText is also display model. :)

> The reportStatusLine script reports "Status bar not found". This is perhaps correct, because there is no status line object.

There doesn't have to be a separate object in this case. Even so, it should just be saying blank. I see that script still uses flatReviewPosition, where it should probably instead use review.getScreenPosition.

> When you activate the review mode and go directly to the last line, NVDA stucks on something that is reported as empty or blank.
> 
> However if you are reviewing line by line, the bottom line can be read.

Interesting. How many blank lines are there after the status line?

Comment 6 by lpintes (in reply to comment 5) on 2014-12-01 10:19

> Interesting. How many blank lines are there after the status line?

I noticed four things here:
1. a "~" as an indication of end of text
2. the normal text, a text one is currently editing
3. a garbage, i.e. the display model is updated incorrectly for some reason
4. one empty line above "Press enter or type command to continue", the screen may be cleared in this case, I am not sure, so this look like one empty line

But the thing I reported, i.e. it is impossible to go directly to the bottom of the flat review, because NVDA is stuck on something "blank" is true in any case.

CC @JosephSL @jcsteh 

In #6769, @FeerRenrut posted some relevant tickets for this:

> *   Unable to find status bar in Visual Studio Ref bug [#5618](https://github.com/nvaccess/nvda/issues/5618)
> *   Unable to find status bar in Office aplications [#649](https://github.com/nvaccess/nvda/issues/649)
> *   Status bar handling does not work with Java Access Bridge applications [#5674](https://github.com/nvaccess/nvda/issues/5674)


@leonardder, @josephsl, @jcsteh any updates on this issue?
@ABuffEr you have created the add-on toolbars explorer. I think your thoughts are very appreciated in this regard. Thanks.
Hi,

We may need to gather more data before proceeding with this. Thanks.
Hi,

We may need to gather more data before proceeding with this. Thanks.
What type of data?

Rui Fontes
NVDA portuguese team


Às 23:48 de 04/01/2019, Joseph Lee escreveu:
> Hi,
> 
> We may need to gather more data before proceeding with this. Thanks.
> 
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub 
> <https://github.com/nvaccess/nvda/issues/4640#issuecomment-451602894>, 
> or mute the thread 
> <https://github.com/notifications/unsubscribe-auth/APIHxkXFGkDmmLwloHNMKFSE2BRqNXb-ks5u_-hSgaJpZM4OZAN2>.
> 


Resolving patch:
diff --git a/source/api.py b/source/api.py
--- a/source/api.py
+++ b/source/api.py
@@ -322,9 +322,13 @@ def getStatusBar():
 	@return: The status bar object or C{None} if no status bar was found.
 	@rtype: L{NVDAObjects.NVDAObject}
 	"""
+	foreground = getForegroundObject()
+	try:
+		return foreground.appModule.statusBar
+	except NotImplementedError:
+		pass
 	# The status bar is usually at the bottom of the screen.
 	# Therefore, get the object at the bottom left of the foreground object using screen coordinates.
-	foreground = getForegroundObject()
 	location=foreground.location
 	if not location:
 		return None
diff --git a/source/appModuleHandler.py b/source/appModuleHandler.py
--- a/source/appModuleHandler.py
+++ b/source/appModuleHandler.py
@@ -497,6 +497,27 @@ def dumpOnCrash(self):
 			self.helperLocalBindingHandle, path)
 		print("Dump path: %s" % path)
 
+	def _get_statusBar(self):
+		"""Retrieve the status bar object of the application.
+		If C{NotImplementedError} is raised, L{api.getStatusBar} will resort to
+		perform a lookup by position.
+		If C{None} is returned, L{GlobalCommands.script_reportStatusLine} will
+		in turn resort to reading the bottom line of text written to the
+		display.
+		@rtype: NVDAObject
+		"""
+		raise NotImplementedError()
+
+	def _get_statusBarTextInfo(self):
+		"""Retrieve a L{TextInfo} positioned at the status bar of the application.
+		This is used by L{GlobalCommands.script_reportStatusLine} in cases where
+		L{api.getStatusBar} could not locate a proper L{NVDAObject} for the
+		status bar.
+		For this method to get called, L{_get_statusBar} must return C{None}.
+		@rtype: TextInfo
+		"""
+		raise NotImplementedError()
+
 class AppProfileTrigger(config.ProfileTrigger):
 	"""A configuration profile trigger for when a particular application has focus.
 	"""
diff --git a/source/globalCommands.py b/source/globalCommands.py
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -1461,17 +1461,22 @@ def script_reportStatusLine(self,gesture):
 		if obj:
 			text = api.getStatusBarText(obj)
 			api.setNavigatorObject(obj)
-			found=True
+			found = True
 		else:
-			info=api.getForegroundObject().flatReviewPosition
+			foreground = api.getForegroundObject()
+			try:
+				info = foreground.appModule.statusBarTextInfo
+			except NotImplementedError:
+				info = foreground.flatReviewPosition
+				if info:
+					info.expand(textInfos.UNIT_STORY)
+					info.collapse(True)
+					info.expand(textInfos.UNIT_LINE)
 			if info:
-				info.expand(textInfos.UNIT_STORY)
-				info.collapse(True)
-				info.expand(textInfos.UNIT_LINE)
-				text=info.text
+				text = info.text
 				info.collapse()
 				api.setReviewPosition(info)
-				found=True
+				found = True
 		if not found:
 			# Translators: Reported when there is no status line for the current program or window.
 			ui.message(_("No status line found"))


Test patch: