Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3 ➡️ 5 #377

Merged
merged 7 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/ignition/gui/MainWindow.hh
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,19 @@ namespace ignition
signals: void configChanged();

/// \brief Displays a message to the user
/// The message will appear in a snackbar, this message requires to
/// click on the botton "Dismiss" to close the dialog.
signals: void notify(const QString &_message);

/// \brief Displays a message to the user
/// The message will appear in a snackbar, this message disappear when
/// the duration is over, or if the user clicks outside or escape before
/// that.
/// \param[in] _message Message to show
/// \param[in] _duration Time in milliseconds that the message will
/// appear
signals: void notifyWithDuration(const QString &_message, int _duration);

/// \internal
/// \brief Private data pointer
private: std::unique_ptr<MainWindowPrivate> dataPtr;
Expand Down
169 changes: 169 additions & 0 deletions include/ignition/gui/qml/IgnSnackBar.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Dialogs 1.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2

/*
To use the snackbar you need to call the methods in the MainWindow class:
- notify(message)
- notifyWithDuration(message, duration)

For example:
// This code will show the message "Message" during one second
App()->findChild<MainWindow *>()->notifyWithDuration("Message", 1000);

// This code will show the message "Message2" but the dialog will be there
// until you press the button "Dismiss"
App()->findChild<MainWindow *>()->notifyWithDuration("Message2");
*/

Popup {
id: snackbar
modal: duration == 0
focus: duration == 0
x: (window.width - width) / 2
y: window.height - window.height / 6
width: window.width - window.width / 6
contentHeight: Math.max(dismissButton.height, notificationText.height)
padding: 10

// If the popup has a Dismiss button, only close by pressing that.
// Otherwise, use the default behavior.
closePolicy: duration == 0 ? Popup.NoAutoClose :
Popup.CloseOnEscape | Popup.CloseOnPressOutside

// Array that contains a dictionary with two keys "text" and "duration"
// This structure keeps the message to show using FIFO
property var popupArray: []

// Duration of the snackbar. If duration is equal to zero then
// you should click on the button "Dismiss" to close the dialog",
// otherwise you need to wait the duration defined.
property int duration: 0

// This method is called when the dialog is closed
onClosed: {
timer.stop()
checkArray();
}

background: Rectangle {
color: Material.background
layer.enabled: true
layer.effect: DropShadow {
color: "#aa000000"
samples: 9
spread: 0
radius: 8.0
}
}

// this function is called when notify() or notifyWithDuration() are called
function setTextDuration(_message, _duration) {
popupArray.push({"text": _message, "duration": _duration})
checkArray();
}

// This method check if the popupArray has remaining messages to show.
function checkArray()
{
if (popupArray.length == 0)
{
return
}

if(!timer.running)
{
if (popupArray.length > 0)
{
var values = popupArray[0]
notificationText.text = values.text
duration = values.duration
snackbar.open()

// Note that objects cannot be individually added to or removed from
// the list once created; to modify the contents of a list, it must be
// reassigned to a new list.
var newpopupArray = []
for (var i = 1; i < popupArray.length; i++)
{
newpopupArray.push(popupArray[i])
}

if (newpopupArray != undefined)
{
popupArray = newpopupArray
}
else
{
popupArray = []
}
if (duration > 0)
{
timer.restart()
}
}
}
}

contentItem: RowLayout {
id: contentLayout
height: dismissButton.height
anchors.verticalCenter: snackbar.verticalCenter

Text {
id: notificationText
color: Material.theme == Material.Light ? "black" : "white"
wrapMode: Text.Wrap
font.pixelSize: 15
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
}
Button {
id: dismissButton
visible: duration == 0
flat: true
Layout.margins: 0
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
background: Rectangle {
color: parent.down ? Material.color(Material.accent, Material.Shade400) :
(parent.hovered ? Material.color(Material.accent, Material.Shade200) :
"transparent")
}
font.pixelSize: 12
text: "Dismiss"
onClicked: snackbar.close()
}
}
Timer {
id: timer
interval: snackbar.duration
onTriggered: {
if (!running) {
snackbar.close();
}
checkArray();
}
}

}
16 changes: 9 additions & 7 deletions include/ignition/gui/qml/IgnSplit.qml
Original file line number Diff line number Diff line change
Expand Up @@ -276,14 +276,15 @@ SplitView {
* Callback when the children array has been changed.
*/
onChildrenChanged: {
if (children.length === 0)
return;

// Propagate child's minimum size changes to the item.
Layout.minimumWidth = Qt.binding(function() {
newItem.Layout.minimumWidth = Qt.binding(function() {
if (children.length === 0 || children[0] === undefined)
return 0;
return children[0].Layout.minimumWidth
});
Layout.minimumHeight = Qt.binding(function() {
newItem.Layout.minimumHeight = Qt.binding(function() {
if (children.length === 0 || children[0] === undefined)
return 0;
return children[0].Layout.minimumHeight
});
}
Expand Down Expand Up @@ -357,8 +358,9 @@ SplitView {
Layout.minimumWidth = child.Layout.minimumWidth;
}
heightSum += child.height;
minHeightSum += child.height < child.Layout.minimumHeight ?
child.height : child.Layout.minimumHeight;

var collapsed = child.Layout.maximumHeight == 50
minHeightSum += collapsed ? child.height : child.Layout.minimumHeight
}

// Minimum height to show all children
Expand Down
29 changes: 5 additions & 24 deletions include/ignition/gui/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,10 @@ ApplicationWindow
Connections {
target: MainWindow
onNotify: {
notificationText.text = _message
notificationDialog.open()
notificationDialog.setTextDuration(_message, 0)
}
onNotifyWithDuration: {
notificationDialog.setTextDuration(_message, _duration)
}
}

Expand Down Expand Up @@ -319,29 +321,8 @@ ApplicationWindow
}
}

/**
* TODO: change to a snackbar / toast
*/
Dialog {
IgnSnackBar {
id: notificationDialog
modal: true
focus: true
x: (window.width - width) / 2
y: window.height / 6
width: Math.min(window.width, window.height) / 3 * 2
contentHeight: notificationColumn.height

Column {
id: notificationColumn
spacing: 20

Label {
id: notificationText
width: notificationDialog.availableWidth
wrapMode: Label.Wrap
font.pixelSize: 18
}
}
}

Timer {
Expand Down
2 changes: 2 additions & 0 deletions include/ignition/gui/resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<file>qml/IgnCardSettings.qml</file>
<file>qml/IgnHelpers.qml</file>
<file>qml/IgnRulers.qml</file>
<file>qml/IgnSnackBar.qml</file>
<file>qml/IgnSortFilterModel.qml</file>
<file>qml/IgnSpinBox.qml</file>
<file>qml/IgnSplit.qml</file>
Expand All @@ -27,6 +28,7 @@
<!-- This qmldir file defines a QML module that can be imported -->
<file alias="qmldir">qml/qmldir</file>
<!-- Add any QML components referenced in the qmldir file here -->
<file alias="IgnSnackBar.qml">qml/IgnSnackBar.qml</file>
<file alias="IgnSpinBox.qml">qml/IgnSpinBox.qml</file>
</qresource>
</RCC>
4 changes: 4 additions & 0 deletions src/Application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ Application::Application(int &_argc, char **_argv, const WindowType _type)
{
igndbg << "Initializing application." << std::endl;

this->setOrganizationName("Ignition");
this->setOrganizationDomain("ignitionrobotics.org");
this->setApplicationName("Ignition GUI");

// Configure console
common::Console::SetPrefix("[GUI] ");

Expand Down
Loading