I have been trying to understand the intent and potential use case to use "Two finger flick right" as explained under Voice over help section on Apple TV. So far my understanding is
Two finger flick right allows voice over to focus on something that is otherwise non-focusable, without moving system focus.
In case of attached screen shot, a user is able to get hint for focus menu option and know more about the option (in this case it will be "Two finger flick right" under the Apple TV logo.
I have been trying to find a good use case where someone may have used it or is aware of any documentation around it's implementation.
This is what my code looks like so far in terms of accessibility attributes
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as? AccessibilityTableViewCell else {
return UITableViewCell()
}
cell.titleLabel.text = "Main title for row \(indexPath.row)"
cell.titleLabel.textColor = .black
cell.isAccessibilityElement = true
cell.accessibilityLabel = "Main title for row \(indexPath.row)"
cell.accessibilityHint = "Hint for Main title at row for index \(indexPath.row)"
cell.accessibilityTraits |= UIAccessibilityTraitStaticText
return cell
}
What additional needs to be set to make the hint label as related content to row 2?
Related
I have NSOutlineViews with floating group rows as a heading. My headers are semi-transparent so scrolled content can just be seen underneath:
In Big Sur, the headers look like this:
They have an underline below them, and all transparency is removed - so it looks as though a background is being added somewhere.
I am subclassing NSTableRowView and NSOutlineView to stop any drawing:
override func draw(_ dirtyRect: NSRect) {}
I am disabling 'Group Style' on the NSTableRowView too:
override var isGroupRowStyle: Bool { get { false } set {} }
Can anyone tell me where the line and background may be coming from? Thank you.
EDIT
Further investigation shows the background and line rendering is part of the NSScrollView floating content view (_NSScrollViewFloatingSubviewsContainerView). There don't seem to be any options associated with this.
The following extreme hack works currently for me to change the background of the floating header in a NSTableView, and to remove the underline:
extension YourViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int) {
if row == 0, rowView.isGroupRowStyle {
DispatchQueue.main.async {
if let scroll = tableView.enclosingScrollView {
for sub in scroll.subviews {
if sub.className.contains("Float"),
let effect = sub.subviews.first?.subviews.first?.subviews.first as? NSVisualEffectView,
let line = sub.subviews.first?.subviews.first?.subviews.last {
effect.material = .menu
line.isHidden = true
}
}
}
}
}
}
}
When row 0 is added, the sub views of the .enclosingScrollView of the tableview are being iterated (after giving the tableview some time to render the floating header by calling the rest of the code async) until we encounter the views we want to change.
Of course, this kind of code is an example of 'bad practice', and will stop working as soon as Apple changes the implementation of the sticky header, but I don't expect anything to crash due to all specific conditions.
In the meanwhile it's probably the best idea to file a Feedback with a request to make the floating header of NSTableView and NSOutlineView adjustable.
The Qt desktop app I'm writing contains a QCombobox in the UI (made with Designer). After I select the QCombobox, I can change the selected item by scrolling the mouse wheel, or by pressing the up/down arrows on the keyboard. That all works fine.
When navigating using the keyboard down arrow, for example, when I reach the bottom item in the list, the down arrow no longer changes the selected item. I understand that this is the expected behavior.
But for this particular QComboBox I'd like to be able to keep pressing the down arrow after reaching the final item in the list, and "wrap" back to the first item, so I can continue to cycle through the items. I have studied the documentation for QComboBox at https://doc.qt.io/qt-5/qcombobox.html and for QAbstractItemModel at https://doc.qt.io/qt-5/qabstractitemmodel.html, but I could not discover any way to achieve what I want here.
Ideally I'd prefer a solution that works for keyboard arrow navigation, for mouse scroll wheel navigation, and for any other UI gesture that might try to activate the "next" or "previous" item in the QComboBox.
I didn't try this solution, but I'm guessing that it's right by intuition.
I think you need to do:
Override keyPressEvent(QKeyEvent *e) to detect up and down arrows.
If the down arrow is pressed, check if it's the last index using currentIndex() const function, compared to the size of the combo box itself.
If so, change the current index to the first one using setCurrentIndex(int index).
Do the same for up arrow if you reached the first index.
P.S. As currentIndex() returned the index after pressing, this might make it jump from the penultimate index to the first one. Thus, I suggest using a private boolean member to be toggled when the condition is met for the first time.
I hope this solution helps you.
The full solution to this problem has a few different aspects.
When the QComboBox is expanded to show all the items, an elegant semantic solution is to override the QAbstractItemView::moveCursor() method. This part of the solution does not require low level event handlers because moveCursor() encapsulates the concept of "next" and "previous". Sadly this only works when the QComboBox is expanded. Note that the items are not actually activated during navigation in this case, until another gesture like a click or enter occurs.
When the QComboBox is collapsed to show one item at a time (the usual case), we have to resort to the low level approach of capturing each relevant gesture, as sketched in the answer by Mohammed Deifallah. I wish Qt had a similar abstraction here analogous to QAbstractItemView::moveCursor(), but it does not. In the code below we capture key press and mouse wheel events, which are the only gestures I'm aware of at the moment. If other gestures are also needed, we would need to independently implement each one. Because the Qt architects did not generalize the concepts of "next" and "previous" for these cases the way they did for QAbstractItemView::moveCursor().
The following code defines a replacement class for QComboBox that implements these principles.
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Qt
# CircularListView allows circular navigation when the ComboBox is expanded to show all items
class CircularListView(QtWidgets.QListView):
"""
CircularListView allows circular navigation.
So moving down from the bottom item selects the top item,
and moving up from the top item selects the bottom item.
"""
def moveCursor(
self,
cursor_action: QtWidgets.QAbstractItemView.CursorAction,
modifiers: Qt.KeyboardModifiers,
) -> QtCore.QModelIndex:
selected = self.selectedIndexes()
if len(selected) != 1:
return super().moveCursor(cursor_action, modifiers)
index: QtCore.QModelIndex = selected[0]
top = 0
bottom = self.model().rowCount() - 1
ca = QtWidgets.QAbstractItemView.CursorAction
# When trying to move up from the top item, wrap to the bottom item
if index.row() == top and cursor_action == ca.MoveUp:
return self.model().index(bottom, index.column(), index.parent())
# When trying to move down from the bottom item, wrap to the top item
elif index.row() == bottom and cursor_action == ca.MoveDown:
return self.model().index(top, index.column(), index.parent())
else:
return super().moveCursor(cursor_action, modifiers)
class CircularCombobox(QtWidgets.QComboBox):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
view = CircularListView(self.view().parent())
self.setView(view)
def _activate_next(self) -> None:
index = (self.currentIndex() + 1) % self.count()
self.setCurrentIndex(index)
def _activate_previous(self):
index = (self.currentIndex() - 1) % self.count()
self.setCurrentIndex(index)
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
if event.key() == Qt.Key_Down:
self._activate_next()
elif event.key() == Qt.Key_Up:
self._activate_previous()
else:
super().keyPressEvent(event)
def wheelEvent(self, event: QtGui.QWheelEvent) -> None:
delta = event.angleDelta().y()
if delta < 0:
self._activate_next()
elif delta > 0:
self._activate_previous()
Is there any way to change the AccessibilityLabel of a WKInterfaceController title?
Or to turn off just the title VoiceOver?
I have shortened the title to X|Y to show the user what they are viewing but this is not very user friendly for someone using VoiceOver as it reads as "X bar Y" when it should either be turned off or read "Item X of Y"
I am doing a hack right now to check for VoiceOver and set the title but this obviously will not display well on a limited size Apple Watch screen:
let voiceOverON: Bool = WKAccessibilityIsVoiceOverRunning()
if voiceOverON
{
self.setTitle("Exercise \(itemSelected + 1) of \(itemsTotal).")
}
else
{
self.setTitle("\(itemSelected + 1)|\(itemsTotal)")
}
Any ideas that might help the visually impaired?
Thanks
RADAR filed: 28355257 - WKInterfaceController does not support Accessibility
Currently I have a project that is being used to draw rooms with lines and images that can be selected by the user by hitting a button representing what they want to add. I.E. if they want a shower a button for shower is pressed and an image appears in a pane. The shower can be resized and moved in the pane. The user also has the ability to use lines to draw objects or walls. The lines can be resized, rotated, or moved. I am now trying to get these objects to interact with each other. Say a user is using lines to make an object, and when the line comes near another object the line being moved snaps to the other object. I have found a 3rd party library that has SnapLineSnapResult but I don't see anything where someone has used it. Is this something that is desktop JavaFX usable, or is it a touch operation and does anyone have code to model or another solution?
SnapLineSnapResult
My code for line that would be useful if I can use this class is as follows:
line.setOnMouseDragged((MouseEvent event1) -> {
// in resize region
if (isInResizeZoneLine(line, event1)) {
// adjust line
line.setEndX(event1.getX());
}
// in movable region
else {
Point2D currentPointer = new Point2D(event1.getX(), event1.getY());
if (bound.getBoundsInLocal().contains(currentPointer)) {
/*--------*/ // potential place for if (near other object to be snapped)
double lineWidth = line.getEndX() - line.getStartX();
line.setStartY(currentPointer.getY());
line.setEndY(currentPointer.getY());
line.setStartX(currentPointer.getX() - lineWidth/2);
line.setEndX(currentPointer.getX() + lineWidth/2);
}
}
});
I have a couple of OptionGroups with very long captions that run across the width of the page, which looks very bad. I tried restricting the width of the OptionGroup using setWidth and via CSS, and also tried restricting the width of the parent container; all without effect.
So I made a grid layout with an option group in the first column (spanning all rows), and individual labels for the captions in the second column (one per row). However, in case the captions span multiple lines (which they do in my case), this leads to the radio buttons / checkboxes no longer being aligned to the captions. (Regrettably, I'm not allowed to post images.) For instance,
(o) This is a multiline
(o) caption
This is another multiline
caption
I resolved this by creating one OptionGroup per label, and adding each option group in the first column:
(o) This is a multiline
caption
(o) This is another multiline
caption
Clearly, in case of radio buttons, this means multiple buttons can be selected at the same time, since they are no longer linked via a single OptionGroup. Therefore, I registered listeners which, each time a button is selected, de-select all other buttons. And this brings me to my problem; since this "unchecking" is done at the server side, there will unavoidably be some lag, meaning that for some time, multiple radio buttons will appear selected at the client side.
Any ideas on how to resolve this? I only started working with Vaadin recently, so I'm far from an expert. Is there some simple way of restricting the caption width (some magical undocumented CSS class), or do I need to extend / adapt the client-side widget implementation?
Thanks,
William
What you need is FlexibleOptionGroup add-on.
Here is an example implementation:
#Override
protected void init(VaadinRequest request) {
Container cont = new IndexedContainer();
cont.addContainerProperty("caption", String.class, "");
// very long strings in the following setValue() methods
cont.getContainerProperty(cont.addItem(), "caption").setValue("I have...");
cont.getContainerProperty(cont.addItem(), "caption").setValue("So I ma...");
FlexibleOptionGroup fog = new FlexibleOptionGroup(cont);
fog.setCaption("FlexibleOptionGroup:");
fog.setItemCaptionPropertyId("caption");
fog.setMultiSelect(true); // force using CheckBoxes
VerticalLayout fogLayout = new VerticalLayout();
Iterator<FlexibleOptionGroupItemComponent> iter;
iter = fog.getItemComponentIterator();
while(iter.hasNext()) {
// OptionGroupItem part (CheckBox or RadioButton)
FlexibleOptionGroupItemComponent fogItemComponent = iter.next();
// CustomComponent part
Label caption = new Label(fogItemComponent.getCaption());
caption.setWidth(400, Unit.PIXELS);
fogLayout.addComponent(new HorizontalLayout(fogItemComponent, caption));
}
setContent(fogLayout);
}
The above code produces: