Browse Source

Made a proper select event to allow selecting list items by keyboard.

David Piuva 5 years ago
parent
commit
705574b1ed

+ 4 - 0
Source/DFPSR/api/guiAPI.cpp

@@ -190,6 +190,10 @@ void dsr::component_setKeyTypeEvent(const Component& component, const KeyboardCa
 	MUST_EXIST(component, component_setKeyTypeEvent);
 	component->keyTypeEvent() = keyboardEvent;
 }
+void dsr::component_setSelectEvent(const Component& component, const IndexCallback& selectEvent) {
+	MUST_EXIST(component, component_setKeyTypeEvent);
+	component->selectEvent() = selectEvent;
+}
 
 bool dsr::component_hasProperty(const Component& component, const ReadableString& propertyName) {
 	MUST_EXIST(component, component_hasProperty);

+ 3 - 0
Source/DFPSR/api/guiAPI.h

@@ -219,6 +219,9 @@ namespace dsr {
 	//   Raises an exception if component doesn't exist.
 	//   There's usually a second's delay before quickly repeating.
 	void component_setKeyTypeEvent(const Component& component, const KeyboardCallback& keyboardEvent);
+	// Select events are sent when the selected index of something has changed.
+	//   Used in Listbox to cover updates from all different ways the selection may change.
+	void component_setSelectEvent(const Component& component, const IndexCallback& selectEvent);
 
 // Theme
 	// Apply the given theme recursively to all components in the window's interface.

+ 2 - 0
Source/DFPSR/gui/InputEvent.h

@@ -119,6 +119,8 @@ public:
 // The callback templates and types
 static std::function<void()> emptyCallback = []() {};
 using EmptyCallback = decltype(emptyCallback) ;
+static std::function<void(int)> indexCallback = [](int64_t index) {};
+using IndexCallback = decltype(indexCallback);
 static std::function<void(int, int)> sizeCallback = [](int width, int height) {};
 using SizeCallback = decltype(sizeCallback);
 static std::function<void(const KeyboardEvent&)> keyboardCallback = [](const KeyboardEvent& event) {};

+ 1 - 0
Source/DFPSR/gui/VisualComponent.h

@@ -117,6 +117,7 @@ public:
 	DECLARE_CALLBACK(keyDownEvent, keyboardCallback);
 	DECLARE_CALLBACK(keyUpEvent, keyboardCallback);
 	DECLARE_CALLBACK(keyTypeEvent, keyboardCallback);
+	DECLARE_CALLBACK(selectEvent, indexCallback);
 private:
 	std::shared_ptr<VisualComponent> getDirectChild(const IVector2D& pixelPosition, bool includeInvisible);
 public:

+ 33 - 7
Source/DFPSR/gui/components/ListBox.cpp

@@ -47,7 +47,10 @@ Persistent* ListBox::findAttribute(const ReadableString &name) {
 	}
 }
 
-ListBox::ListBox() {}
+ListBox::ListBox() {
+	// Changed from nothing to zero
+	this->callback_selectEvent(0);
+}
 
 bool ListBox::isContainer() const {
 	return true;
@@ -166,7 +169,7 @@ void ListBox::receiveMouseEvent(const MouseEvent& event) {
 		this->hasImages = false; // Force redraw
 	} else if (event.mouseEventType == MouseEventType::MouseUp) {
 		if (this->pressedIndex > -1 && this->inside && !onScrollBar && hoverIndex == this->pressedIndex) {
-			this->selectedIndex.value = hoverIndex;
+			this->setSelectedIndex(hoverIndex, false);
 			this->limitScrolling(true);
 			this->callback_pressedEvent();
 		}
@@ -195,6 +198,21 @@ void ListBox::receiveMouseEvent(const MouseEvent& event) {
 	}
 }
 
+void ListBox::receiveKeyboardEvent(const KeyboardEvent& event) {
+	if (event.keyboardEventType == KeyboardEventType::KeyDown) {
+		int contentLength = this->list.value.length();
+		int oldIndex = this->selectedIndex.value;
+		if (contentLength > 1) {
+			if (oldIndex > 0 && event.dsrKey == DsrKey::DsrKey_UpArrow) {
+				this->setSelectedIndex(oldIndex - 1, true);
+			} else if (oldIndex < contentLength - 1 && event.dsrKey == DsrKey::DsrKey_DownArrow) {
+				this->setSelectedIndex(oldIndex + 1, true);
+			}
+		}
+	}
+	VisualComponent::receiveKeyboardEvent(event);
+}
+
 void ListBox::loadTheme(VisualTheme theme) {
 	this->scalableImage_listBox = theme_getScalableImage(theme, U"ListBox");
 	this->scalableImage_scrollButton = theme_getScalableImage(theme, U"ScrollButton");
@@ -236,12 +254,20 @@ void ListBox::changedAttribute(const ReadableString &name) {
 	this->hasImages = false;
 	if (string_caseInsensitiveMatch(name, U"List")) {
 		// Reset selection on full list updates
-		this->selectedIndex.value = 0;
+		this->setSelectedIndex(0, true);
 	}
-	this->limitSelection();
+	this->limitSelection(false);
 	this->limitScrolling();
 }
 
+void ListBox::setSelectedIndex(int index, bool forceUpdate) {
+	if (forceUpdate || this->selectedIndex.value != index) {
+		this->selectedIndex.value = index;
+		this->hasImages = false;
+		this->callback_selectEvent(index);
+	}
+}
+
 int64_t ListBox::getSelectedIndex() {
 	int64_t index = this->selectedIndex.value;
 	if (this->selectedIndex.value < 0 || this->selectedIndex.value >= this->list.value.length()) {
@@ -250,7 +276,7 @@ int64_t ListBox::getSelectedIndex() {
 	return index;
 }
 
-void ListBox::limitSelection() {
+void ListBox::limitSelection(bool indexChangedMeaning) {
 	// Get the maximum index
 	int64_t maxIndex = this->list.value.length() - 1;
 	if (maxIndex < 0) {
@@ -258,7 +284,7 @@ void ListBox::limitSelection() {
 	}
 	// Reset selection on out of bound
 	if (this->selectedIndex.value < 0 || this->selectedIndex.value > maxIndex) {
-		this->selectedIndex.value = 0;
+		setSelectedIndex(0, indexChangedMeaning);
 	}
 }
 
@@ -343,7 +369,7 @@ String ListBox::call(const ReadableString &methodName, const ReadableString &arg
 			throwError("Index (", arguments, " = ", index, ") out of bound in RemoveElement!\n");
 		}
 		this->list.value.remove(index);
-		this->limitSelection();
+		this->limitSelection(true);
 		this->limitScrolling(true);
 		this->hasImages = false;
 		return U"";

+ 4 - 1
Source/DFPSR/gui/components/ListBox.h

@@ -61,7 +61,7 @@ private:
 	// Helper methods
 	// Returns the selected index referring to an existing element or -1 if none is selected
 	int64_t getSelectedIndex();
-	void limitSelection(); // Clamp selection to valid range
+	void limitSelection(bool indexChangedMeaning); // Clamp selection to valid range
 	void limitScrolling(bool keepSelectedVisible = false); // Clamp scrolling
 	int64_t getVisibleScrollRange(); // Return the number of items that are visible at once
 	IRect getScrollBarLocation_includingButtons(); // Return the location of the scroll-bar with buttons
@@ -69,12 +69,15 @@ private:
 	IRect getKnobLocation(); // Return the location of the scroll-bar's knob
 	void pressScrollBar(int64_t localY); // Press the scroll-bar at localY in pixels
 	void loadTheme(VisualTheme theme);
+	// If a new selection inherited the old index, forceUpdate will send the select event anyway
+	void setSelectedIndex(int index, bool forceUpdate);
 public:
 	ListBox();
 public:
 	bool isContainer() const override;
 	void drawSelf(ImageRgbaU8& targetImage, const IRect &relativeLocation) override;
 	void receiveMouseEvent(const MouseEvent& event) override;
+	void receiveKeyboardEvent(const KeyboardEvent& event) override;
 	void changedTheme(VisualTheme newTheme) override;
 	void changedLocation(const IRect &oldLocation, const IRect &newLocation) override;
 	void changedAttribute(const ReadableString &name) override;

+ 8 - 1
Source/SDK/guiExample/main.cpp

@@ -49,10 +49,17 @@ int main(int argn, char **argv) {
 			}
 		}
 	});
+	// Called when the selected index has changed, when indices have changed their meaning
+	//   Triggered by mouse, keyboard, list changes and initialization
+	component_setSelectEvent(myListBox, [](int64_t index) {
+		String content = component_call(myListBox, U"GetSelectedText");
+		printText("Select event: content is (", content, ") at index ", index, "\n");
+	});
+	// Only triggered by mouse presses like any other component
 	component_setPressedEvent(myListBox, []() {
 		int64_t index = string_toInteger(component_call(myListBox, U"GetSelectedIndex"));
 		String content = component_call(myListBox, U"GetSelectedText");
-		printText("content is (", content, ") at index ", index, "\n");
+		printText("Pressed event: content is (", content, ") at index ", index, "\n");
 	});
 
 	// Execute