Skip to content

Commit 3bc7053

Browse files
authored
Merge 96d8462 into c6aaf78
2 parents c6aaf78 + 96d8462 commit 3bc7053

17 files changed

Lines changed: 247 additions & 9 deletions

File tree

nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,10 +1000,16 @@ VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(
10001000
}
10011001

10021002
BSTR value=NULL;
1003-
if(pacc->get_accValue(varChild,&value)==S_OK) {
1004-
if(value&&SysStringLen(value)==0) {
1005-
SysFreeString(value);
1006-
value=NULL;
1003+
if (pacc->get_accValue(varChild, &value) == S_OK) {
1004+
if (value) {
1005+
if (role == ROLE_SYSTEM_LINK) {
1006+
// For links, store the IAccessible value to handle same page link detection.
1007+
parentNode->addAttribute(L"IAccessible::value", value);
1008+
}
1009+
if (SysStringLen(value) == 0) {
1010+
SysFreeString(value);
1011+
value = NULL;
1012+
}
10071013
}
10081014
}
10091015

source/NVDAObjects/IAccessible/ia2Web.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ def _get_states(self):
213213
if popupState:
214214
states.discard(controlTypes.State.HASPOPUP)
215215
states.add(popupState)
216+
if self.role == controlTypes.Role.LINK and controlTypes.State.LINKED in states and self.linkType:
217+
states.add(self.linkType)
216218
return states
217219

218220
def _get_landmark(self):

source/NVDAObjects/UIA/chromium.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# See the file COPYING for more details.
44
# Copyright (C) 2020-2021 NV Access limited, Leonard de Ruijter
55

6+
67
import UIAHandler
78
from . import web
89
import controlTypes
@@ -66,11 +67,20 @@ def _getControlFieldForUIAObject(self, obj, isEmbedded=False, startOfNode=False,
6667
class ChromiumUIA(web.UIAWeb):
6768
_TextInfo = ChromiumUIATextInfo
6869

70+
def _get_states(self) -> set[controlTypes.State]:
71+
states = super().states
72+
if self.role == controlTypes.Role.LINK and self.linkType:
73+
states.add(self.linkType)
74+
return states
75+
6976

7077
class ChromiumUIATreeInterceptor(web.UIAWebTreeInterceptor):
7178
def _get_documentConstantIdentifier(self):
7279
return self.rootNVDAObject.parent._getUIACacheablePropertyValue(UIAHandler.UIA_AutomationIdPropertyId)
7380

81+
def _get_documentURL(self) -> str | None:
82+
return self.rootNVDAObject.value
83+
7484

7585
class ChromiumUIADocument(ChromiumUIA):
7686
treeInterceptorClass = ChromiumUIATreeInterceptor

source/NVDAObjects/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,3 +1627,18 @@ def _get_isBelowLockScreen(self) -> bool:
16271627
if not isLockScreenModeActive():
16281628
return False
16291629
return _isObjectBelowLockScreen(self)
1630+
1631+
linkType: controlTypes.State | None
1632+
"""Typing information for auto property _get_linkType
1633+
Determines the link type based on the link and document URLs.
1634+
"""
1635+
1636+
def _get_linkType(self) -> controlTypes.State | None:
1637+
if self.role != controlTypes.Role.LINK:
1638+
return None
1639+
from browseMode import BrowseModeDocumentTreeInterceptor
1640+
1641+
ti = getattr(self, "treeInterceptor", None)
1642+
if not isinstance(ti, BrowseModeDocumentTreeInterceptor):
1643+
return None
1644+
return ti.getLinkTypeInDocument(self.value)

source/braille.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@
277277
controlTypes.State.HASCOMMENT: _("cmnt"),
278278
# Translators: Displayed in braille when a control is switched on
279279
controlTypes.State.ON: "⣏⣿⣹",
280+
# Translators: Displayed in braille when a link destination points to the same page
281+
controlTypes.State.INTERNAL_LINK: _("smp"),
280282
}
281283
negativeStateLabels = {
282284
# Translators: Displayed in braille when an object is not selected.

source/browseMode.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import gui.contextHelp
5555
from abc import ABCMeta, abstractmethod
5656
import globalVars
57+
from utils import urlUtils
5758
from typing import Optional
5859

5960

@@ -304,6 +305,15 @@ class BrowseModeTreeInterceptor(treeInterceptorHandler.TreeInterceptor):
304305
scriptCategory = inputCore.SCRCAT_BROWSEMODE
305306
_disableAutoPassThrough = False
306307
APPLICATION_ROLES = (controlTypes.Role.APPLICATION, controlTypes.Role.DIALOG)
308+
documentURL: str | None = None
309+
"""The URL of the current browse mode document.
310+
C{None} when there is no URL or it is unknown.
311+
Used to determine the type of a link in the document.
312+
"""
313+
314+
def getLinkTypeInDocument(self, url: str) -> controlTypes.State | None:
315+
"""Returns the type of a link in the document, or C{None} if the link type cannot be determined."""
316+
return urlUtils.getLinkType(url, self.documentURL)
307317

308318
def _get_currentNVDAObject(self):
309319
raise NotImplementedError

source/config/configSpec.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@
242242
# 0: Off, 1: style, 2: color and style
243243
reportCellBorders = integer(0, 2, default=0)
244244
reportLinks = boolean(default=true)
245+
reportLinkType = boolean(default=true)
245246
reportGraphics = boolean(default=True)
246247
reportComments = boolean(default=true)
247248
reportBookmarks = boolean(default=true)

source/controlTypes/processAndLabelStates.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing import Dict, List, Optional, Set
77

88
from .role import Role, clickableRoles
9-
from .state import State, STATES_SORTED
9+
from .state import State, STATES_SORTED, STATES_LINK_TYPE
1010
from .outputReason import OutputReason
1111

1212

@@ -31,6 +31,7 @@ def _processPositiveStates(
3131
positiveStates.discard(State.EDITABLE)
3232
if role != Role.LINK:
3333
positiveStates.discard(State.VISITED)
34+
positiveStates.discard(State.INTERNAL_LINK)
3435
positiveStates.discard(State.SELECTABLE)
3536
positiveStates.discard(State.FOCUSABLE)
3637
positiveStates.discard(State.CHECKABLE)
@@ -47,6 +48,9 @@ def _processPositiveStates(
4748
# or reporting clickable just isn't useful,
4849
# or the user has explicitly requested no reporting clickable
4950
positiveStates.discard(State.CLICKABLE)
51+
if not config.conf["documentFormatting"]["reportLinkType"]:
52+
for state in STATES_LINK_TYPE:
53+
positiveStates.discard(state)
5054
if reason == OutputReason.QUERY:
5155
return positiveStates
5256
positiveStates.discard(State.DEFUNCT)

source/controlTypes/state.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,13 @@ def negativeDisplayString(self) -> str:
102102
HASPOPUP_GRID = setBit(48)
103103
HASPOPUP_LIST = setBit(49)
104104
HASPOPUP_TREE = setBit(50)
105+
INTERNAL_LINK = setBit(51)
105106

106107

107108
STATES_SORTED = frozenset([State.SORTED, State.SORTED_ASCENDING, State.SORTED_DESCENDING])
108109

110+
STATES_LINK_TYPE = frozenset([State.INTERNAL_LINK])
111+
109112

110113
_stateLabels: Dict[State, str] = {
111114
# Translators: This is presented when a control or document is unavailable.
@@ -204,6 +207,9 @@ def negativeDisplayString(self) -> str:
204207
State.HASPOPUP_LIST: _("opens list"),
205208
# Translators: Presented when a control has a pop-up tree.
206209
State.HASPOPUP_TREE: _("opens tree"),
210+
# Translators: Presented when a link destination points to the page containing the link.
211+
# For example, links of a table of contents of a document with different sections.
212+
State.INTERNAL_LINK: _("same page"),
207213
}
208214

209215

source/globalCommands.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,29 @@
125125
NO_SETTINGS_MSG = _("No settings")
126126

127127

128+
def toggleBooleanValue(
129+
configSection: str,
130+
configKey: str,
131+
enabledMsg: str,
132+
disabledMsg: str,
133+
) -> None:
134+
"""
135+
Toggles a boolean value in the configuration and returns the appropriate message.
136+
137+
:param configSection: The configuration section containing the boolean key.
138+
:param configKey: The configuration key associated with the boolean value.
139+
:param enabledMsg: The message for the enabled state.
140+
:param disabledMsg: The message for the disabled state.
141+
:return: None.
142+
"""
143+
currentValue = config.conf[configSection][configKey]
144+
newValue = not currentValue
145+
config.conf[configSection][configKey] = newValue
146+
147+
msg = enabledMsg if newValue else disabledMsg
148+
ui.message(msg)
149+
150+
128151
class GlobalCommands(ScriptableObject):
129152
"""Commands that are available at all times, regardless of the current focus."""
130153

@@ -902,6 +925,21 @@ def script_toggleReportLinks(self, gesture):
902925
config.conf["documentFormatting"]["reportLinks"] = True
903926
ui.message(state)
904927

928+
@script(
929+
# Translators: Input help mode message for toggle report link type command.
930+
description=_("Toggles on and off the reporting of link type"),
931+
category=SCRCAT_DOCUMENTFORMATTING,
932+
)
933+
def script_toggleReportLinkType(self, gesture: inputCore.InputGesture):
934+
toggleBooleanValue(
935+
configSection="documentFormatting",
936+
configKey="reportLinkType",
937+
# Translators: The message announced when toggling the report link type document formatting setting.
938+
enabledMsg=_("Report link type on"),
939+
# Translators: The message announced when toggling the report link type document formatting setting.
940+
disabledMsg=_("Report link type off"),
941+
)
942+
905943
@script(
906944
# Translators: Input help mode message for toggle report graphics command.
907945
description=_("Toggles on and off the reporting of graphics"),

0 commit comments

Comments
 (0)