Suppress child node from being informed about an event without consuming the same one - user-interface

Situation
Let's say you have a Scene which contains a single complex control that itself contains a TextArea. The complex control is composed in a way, that when it gains the focus the TextArea gains the focus.
The ComplexControl has the ability to make the TextArea non-editable. Then a key input can has another semantic than operating a text. The new semantic can be defined by the ComplexControl or any node soemwhat higher in the scene graph.
The Scene has a global shortcut CTRL+N for opening a new Tab/View. In context of the ComplexControl CRTL+N semantic is creating a new Text document.
------------------------
| Scene |
| ------------------ |
| | ComplexControl | |
| | ------------ | |
| | | TextArea | | |
| | ------------ | |
| ------------------ |
------------------------
Objective
React globally or by a node somewhat higher in the scene graph on KeyEvents / KeyCombinations. Any control somewhat lower can take over the events, so that in a more special context an occurred event has more relevance as globally defined.
Suggestion
Setting a KeyHandler onto the Scene or a higher Node. Any EventHandler closer to the Source/Target of the EventDispatchChain" can consume the event. This way the KeyHandler can prevent the Scene or any higher Node from reacting on the key input, which is what the user intends in the special context of a control. Therefore an EventFilter at the higher place is not suitable.
Trouble
The TextArea consumes always any key event, even if it's not the intention of the design. No EventHandler higher in the EventDispatchChain gets informed.
Problem
How can it be forced to give the event back from the TextArea without consuming it and then let it bubble up the EventDispatchChain?
How can delivering the Event down to the TextArea be prevented without consuming it, so that it not even knows anything about the event.

Thanx to sillyfly, who showed me the way. As an answer to question 2, here's the resulting source. I decided to place the functionality at a central point for reuse and give the opportunity to make the suppression condition based. Please don't blame me for the class name, that's not the point ;-)
public class EventUtil {
/**
* Prevents Events of the given <b>eventTypes</b> to be passed down during the
* event capturing phase, if the <b>condition</b> is met.
*
* #param node
* The Node, whose descendants should not be informed about the Event
* #param eventTypes
* The types of Events for which the prevention should be valid
* #param condition
* The condition that must be met to prevent passing the Event down
*/
public static void preventPassDown(Node node, Supplier<Boolean> condition, EventType<?>... eventTypes) {
for (EventType<?> eventType : eventTypes) {
if (eventTypes == null) {
return;
}
node.addEventFilter(eventType, event -> {
if (condition.get()) {
event.consume();
Parent parent = node.getParent();
if (parent != null) {
Event.fireEvent(parent, event);
}
}
});
}
}
/**
* Prevents Events of the given <b>eventTypes</b> to be passed down during the
* event capturing phase.
*
* #param node
* The Node, whose descendants should not be informed about the Event
* #param eventTypes
* The types of Events for which the prevention should be valid
*/
public static void preventPassDown(Node node, EventType<?>... eventTypes) {
preventPassDown(node, () -> true, eventTypes);
}
If someone finds an answer to question 1, please feel free to jump in.

Related

NSOutlineView detect when children are collapsed

I'm trying to store the actual collapsed/expanded state of items in a NSOutlineView, such that it can be restored later. There are two methods available on NSOutlineViewDelegate:
outlineViewItemDidExpand(Notification)
outlineViewItemDidCollapse(Notification)
The problem is that these methods are not only called for the item the user clicks on, but for collapsible children as well. Example:
- a
-- b
--- c
When a is collapsed outlineViewItemDidCollapse is called twice, once for b and once for a. Marking both as collapsed is incorrect, since b should still be expanded and c should be visible after expanding a again. So the actual state for b should be expanded.
When a user Option-clicks on a all children are collapsed as well (outlineView.collapseItem(item, collapseChildren: true)). After expanding a again, b should stay collapsed. The state for b should be collapsed in this case.
The two different states:
a: collapsed, b: expanded (but hidden due to parent)
a: collapsed, b: collapsed (and hidden due to parent)
Is there any way to differentiate between these two actions/states, such that I can properly restore it later?
Some ideas:
NSOutlineView can save and restore the expanded items (autosaveExpandedItems). The settings can be retrieved from NSUserDefaults. The key is NSOutlineView Items <autosaveName>.
Subclass NSOutlineView and override expandItem(_:expandChildren:) and collapseItem(_:collapseChildren:). The methods are not called for the children.
It might be possible to figure out which item is expanded or collapsed using the current event in outlineViewItemWillExpand(_:) and outlineViewItemWillCollapse(_:).
Edited to give a revised answer...
Apparently there is no easy way to recover whether a given container is expanded or collapsed once its parent object has been collapsed. Clearly something in the inner workings of the outline view remembers — possibly it's something as simple as storing the state of the cell view's disclosure button cell, or possibly it sets up a flag in the tree controller or its nodes — but in any case there's no direct programmatic interface. I suspect you'll have to keep track of it in the model object.
To do that, add a boolean property to your model item, such as:
#property BOOL currentlyExpanded;
Then you'll want to implement the two delegate methods outlineViewItemDidExpand: and outlineViewItemWillCollapse:, like so (this is assuming you are using a tree controller for the outline view):
- (void)outlineViewItemDidExpand:(NSNotification *)notification {
NSTreeNode * node = [notification.userInfo objectForKey:#"NSObject"];
NSOutlineView * ov = notification.object;
MyModelItem * item = [node representedObject];
/*
because we can only expand a visible container, we merely note
that this container is now expanded in our view. This will be
called for every container that is expanded, so we don't have to
think about it much.
*/
item.currentlyExpanded = YES;
}
- (void)outlineViewItemWillCollapse:(NSNotification *)notification {
NSTreeNode * node = [notification.userInfo objectForKey:#"NSObject"];
NSOutlineView * ov = notification.object;
MyModelItem * item = [node representedObject];
/*
Elements are collapsed from top to bottom. A collapsed parent
means the collapse started someplace farther up the chain than
our current item, so the expansion state of the current item is
not going to change unless the option key is held down, or you
implement a collapseItem:collapseChildren: with the second
parameter as YES. This accounts for the first; you'll have to
deal with the second in code.
*/
BOOL optionKeyIsDown = [[NSApp currentEvent] modifierFlags] && NSEventModifierFlagOption;
if ([ov isItemExpanded:[node parentNode]] || optionKeyIsDown) {
item.currentlyExpanded = NO;
}
}
These should keep the model item property currentlyExpanded synced with the outline view's internal expansion table (whatever that is). If you want to refer to it or store it in a database you can access it straight from the model objects.
The way I handled the bitmask throws a warning, but I'm too lazy to fix it...
Preserving this last part after the edit, because I think it's good info...
Normally you do not have to worry about any of this; NSOutlineView will 'do the right thing' of its own accord. If the user clicks the disclosure triangle of a container and then reopens it, all of the subcontainers will retain their expanded/collapsed states; if a user option-clicks the control triangle, all of the subcontainers will be marked as expanded or collapsed (depending on whether the user is option-opening or option-closing the parent). Don't bother with it unless you want some specialized behavior (which you would generally set up in the delegate methods outlineView:shouldCollapseItem: and outlineView:shouldExpandItem:).
If you're trying to retain the expansion state across app invocations, set the NSOutlineView property autosaveExpandedItems to true. No bookkeeping necessary...

NativeScript gesture state name to corresponding number

I want to use gestures in my nativescript-angular app. When I log this:
console.log("Pinch scale: " + args.scale + " state: " + args.state);
args.state gives me numbers (1,2,3), but in the docs they referred to names such as: began or cancelled. Waht is the corresponding name to each number?
GestureStateTypes are defined like this
export enum GestureStateTypes {
/**
* Gesture canceled.
*/
cancelled,
/**
* Gesture began.
*/
began,
/**
* Gesture changed.
*/
changed,
/**
* Gesture ended.
*/
ended
}
As per enum nature in TS first elemnet will auto assigned 0 if it is not assigned any value and then it will be auto incremented .
so final result will be cancelled=0,began=1,changed=2 and ended=3. but you should avoid directly checking to its value like args.state==1 and instead should use args.state==GestureStateTypes.began.

uiautomatorviewer - What does NAF stand for?

Its my understanding that UIAutomator is unable to automate any elements where NAF = true in uiautomatorviewer. Ive searched high and low but i cant for the life of me find what NAF stands for. Does anyone know?
"Not Accessibility Friendly"
These are UI elements which are apparently interactive, but which don't have accessibility affordances like content descriptions.
From the source for AccessibilityNodeInfoDumper.java (part of uiautomator):
/**
* We're looking for UI controls that are enabled, clickable but have no
* text nor content-description. Such controls configuration indicate an
* interactive control is present in the UI and is most likely not
* accessibility friendly. We refer to such controls here as NAF controls
* (Not Accessibility Friendly)
*
* #param node
* #return false if a node fails the check, true if all is OK
*/
private static boolean nafCheck(AccessibilityNodeInfo node) {
boolean isNaf = node.isClickable() && node.isEnabled()
&& safeCharSeqToString(node.getContentDescription()).isEmpty()
&& safeCharSeqToString(node.getText()).isEmpty();
if (!isNaf)
return true;
// check children since sometimes the containing element is clickable
// and NAF but a child's text or description is available. Will assume
// such layout as fine.
return childNafCheck(node);
}
NAF stands for " Not Accessibility Friendly" .

How to redirect a WM_KEYDOWN message to another control in MFC?

I'm on a roll today with MFC! :D
I have a text box and a list view control.
When the user presses the VK_UP and VK_DOWN keys in the text box, I would like this to happen:
Do some stuff.
Have the list view control also process the message (to highlight the previous/next item).
I want the list view to wrap around the current selection, if the key press is the first in its sequence.
Do some more stuff.
I tried subclassing my edit box in my dialog:
class MyEditBox : public CWnd
{
bool allowWrap;
afx_msg void OnKeyUp(UINT, UINT, UINT) { this->allowWrap = true; }
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CListCtrl &listView = static_cast<CListView *>(
this->GetParent()->GetDlgItem(IDC_LIST_VIEW))->GetListCtrl();
if (nChar == VK_UP || nChar == VK_DOWN)
{
int iSelBefore = listView.GetNextItem(-1, LVNI_SELECTED);
this->GetParent()->GetDlgItem(IDC_LIST_VIEW)
->OnKeyDown(nChar, nRepCnt, nFlags); //Oops! Protected member :(
int iSelAfter = listView.GetNextItem(-1, LVNI_SELECTED);
if (iSelBefore == iSelAfter && // Did the selection reach an end?
this->allowWrap) // If so, can we wrap it around?
{
int i = a == 0 ? listView.GetItemCount() - 1 : 0;
listView.SetItemState(i, LVIS_SELECTED | LVIS_FOCUSED,
LVIS_SELECTED | LVIS_FOCUSED);
}
}
this->allowWrap = false;
}
}
but OnKeyDown() is a protected member, so I can't just call it on another control.
Is there a better way to solve this than manually sending the command with SendMessage? Should I change my design, e.g. subclass something else, etc.?
Your intention is to select previous or next item in list control, right? Then you should call the method to do that directly instaed of asking the CListCtrl to "process" your message.
You may call CListCtrl::SetSelectionMark and CListCtrl::SetItemState to select next or previous keystroke. Example:
cListCtrl.SetSelectionMark(nIndex);
cListCtrl.SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, 0xFF);
You can handle Key Down, Key Up as well as Page Down, Page Up, End, Home or any any key from edit box. You need to do calculation, though.
Or you can just SendMessage. There is no need to call OnKeyDown directly. Let the framework call it for you when you send the message.
I am seeing also other ways to solve this:
Derive a class from CListCtrl called MyListCtrl and choose one of two things:
1.1 Declare MyEditBox as a friend and now you can call the protected methods on MyEditBox
1.2 Add public methods CallOnKeyDown(...) and CallOnKeyup(...) to it that only do what is needed.
And when creating the control, instance a MyListCtrl instead of a CListCtrl. Also replace the listView variable you have shown here to be a MyListCtrl and use the methods you have now available
Use PreTranslateMessage(...). I think this "hammer" solution is worse than sending a message.

Suppress a wxWidgets event at specific point to prevent mutual handling

I have a WxWidget Panel with two TextControls for user input. One TextControl input changes the value of the other input field. I used an EVT_COMMAND_TEXT_UPDATE event and bound it to a function like "OnValueChanged" ...
mTextCtrl1->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(MyClass::OnTextCtrlChanged1), NULL, this);
mTextCtrl2->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(MyClass::OnTextCtrlChanged2), NULL, this);
void MyClass::OnTextCtrlChanged1(wxCommandEvent &event) {
// ...
mTextCtrl2->SetValue(...); // Set a Hex value of entered Value in text ctrl 1
}
void MyClass::OnTextCtrlChanged2(wxCommandEvent &event) {
// ...
mTextCtrl1->SetValue(...); // Set a Integer value of entered Value in text ctrl 2
// at this point, MyClass::OnTextCtrl1 is handled,
// but should not, only if user himself enters something
}
Problem is, when Text in one TextControl is changed, it changes the value of the other correctly. But, as soon as text is changed in other input, it rises its own TEXT_UPDATE event and updates the current users inputs, resulting in funny curser jumping etc.
Is it possible to provide the execution of these events while changing the value of the other TextControl, so that it does not rise its TEXT_UPDATE event? If the user makes some input for himself to this text control, it should work as usual.
Maybe you can use wxTextCtrl::ChangeValue
virtual void ChangeValue(const wxString& value)
Sets the text value and marks the control as not-modified (which means that IsModified would return false immediately after the call to SetValue).
Note that this function will not generate the wxEVT_COMMAND_TEXT_UPDATED event. This is the only difference with SetValue. See this topic for more information.

Resources