Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+210
View File
@@ -0,0 +1,210 @@
#
# $Id$
# $HeadURL$
# $Author$
#
#
# Lts Widgets library
#
SET(ltswidgets_SRCS
#lts_abstract_table_model.h
#lts_checkable_table_model.h
lts_abstract_sequence_widget.cpp
lts_abstract_sequence_widget.h
lts_background_widget.cpp
lts_background_widget.h
lts_base.h
lts_base_layout_widget.cpp
lts_base_layout_widget.h
lts_column_filter_proxy_model.cpp
lts_column_filter_proxy_model.h
lts_combo_box.cpp
lts_combo_box.h
lts_selection_popup.cpp
lts_selection_popup.h
lts_drag_icon.cpp
lts_drag_icon.h
#lts_drop_handler.h
lts_elided_label.cpp
lts_elided_label.h
lts_events.cpp
lts_events.h
lts_frameless_dialog.cpp
lts_frameless_dialog.h
lts_grid_layout.cpp
lts_grid_layout.h
lts_height_plot_widget.cpp
lts_height_plot_widget.h
lts_message_dialog.cpp
lts_message_dialog.h
lts_message_dialog_helper.cpp
lts_message_dialog_helper.h
lts_profile_button.cpp
lts_profile_button.h
lts_progress_dialog_helper.cpp
lts_progress_dialog_helper.h
lts_splitter.cpp
lts_splitter.h
lts_splitter_handle.cpp
lts_splitter_handle.h
lts_tab_bar.cpp
lts_tab_bar.h
lts_tab_widget.cpp
lts_tab_widget.h
lts_tab_sequence_widget.cpp
lts_tab_sequence_widget.h
lts_tool_bar.cpp
lts_tool_bar.h
lts_tool_box.cpp
lts_tool_box.h
lts_tool_bar_area.cpp
lts_tool_bar_area.h
lts_dialog.cpp
lts_dialog.h
lts_dialog_helper.cpp
lts_dialog_helper.h
lts_dialog_button_box.cpp
lts_dialog_button_box.h
lts_operator_menu.cpp
lts_operator_menu.h
lts_image_editor.cpp
lts_image_editor.h
lts_led_widget.cpp
lts_led_widget.h
ui_requirement.cpp
ui_requirement.h
#lts_widgets_export.h
lts_picture_view.cpp
lts_picture_view.h
lts_global_wait_dialog.cpp
lts_global_wait_dialog.h
lts_wait_dialog.cpp
lts_wait_dialog.h
lts_splash.cpp
lts_splash.h
lts_info_dialog.cpp
lts_info_dialog.h
lts_spinbox.cpp
lts_spinbox.h
lts_multi_range_spinbox.cpp
lts_multi_range_spinbox.h
lts_text_editor.cpp
lts_text_editor.h
lts_text_edit_dialog.h
lts_text_edit_dialog.cpp
lts_range_slider.cpp
lts_range_slider.h
lts_notification_box.cpp
lts_notification_box.h
lts_msec_date_time_edit.cpp
lts_msec_date_time_edit.h
lts_bitfield_widget.cpp
lts_bitfield_widget.h
lts_overlay_loading.cpp
lts_angle_edit.cpp
lts_angle_edit.h
lts_text_editor2.h
lts_text_editor2.cpp
LtsImageCropper/lts_image_cropper.cpp
)
#if (TABLET_MODE)
# LIST(APPEND ltswidgets_SRCS
# lts_date_time_edit_touch.h
# lts_date_time_edit_touch.cpp
# )
# NGL_WRAP_UI(ltswidgets_SRCS
# lts_date_time_edit_touch.ui
# )
#else()
LIST(APPEND ltswidgets_SRCS
lts_date_time_edit.h
lts_date_time_edit.cpp
lts_date_time_edit_p.h
lts_date_time_edit_p.cpp
)
#endif()
NGL_WRAP_UI(ltswidgets_SRCS
lts_selection_popup.ui
lts_base_layout.ui
lts_tool_bar_area.ui
lts_overlay_loading.ui
lts_dialog_button_box.ui
lts_operator_menu.ui
lts_dialog_helper.ui
lts_info_dialog.ui
lts_text_editor.ui
lts_notification_box.ui
lts_angle_edit.ui
lts_date_time_edit_touch.ui
)
NGL_ADD_BIG_RESOURCES(ltswidgets_SRCS resources/lts_resources.qrc)
NGL_ADD_LIBRARY(WS TOOLSWS TOOLSSV LtsWidgets SHARED ${ltswidgets_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsWidgets
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsWidgets
PRIVATE
Qt5::Network # for QHostInfo - for portability
Qt5::Svg
ngltools
ngltranslator
TabBarTabStyle
LtsSessionData
LtsUtilsSettings
PUBLIC
LtsUtilsCore
Qt5::Core
Qt5::Widgets
Qt5::Gui
)
NGL_INSTALL_TARGET(LtsWidgets)
#
# Designer-Plugin
#
SET(ltswidgetsplugin_SRCS ltswidgetsplugin.cpp ltswidgetsplugin.json)
NGL_ADD_LIBRARY(WS ltswidgetsplugin MODULE ${ltswidgetsplugin_SRCS})
NGL_TARGET_LINK_LIBRARIES(ltswidgetsplugin LtsWidgets ltsCoordWidget LtsDataView LtsFileDialog LtsReportDisplay svcFlmCommon)
NGL_SET_TARGET_PROPERTIES(ltswidgetsplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/designer)
NGL_INSTALL_TARGET(ltswidgetsplugin DESTINATION lib/designer COMPONENT Devel)
IF(BUILD_TESTING)
# picture_editing_test
SET(pet_SRCS
tests/lts_picture_editing.cpp
tests/lts_picture_editing.h
tests/lts_picture_editing_main.cpp
)
NGL_WRAP_UI(pet_SRCS
tests/lts_picture_editing.ui
)
NGL_ADD_EXECUTABLE(WS NO_TRANSLATION picture_editing_test ${pet_SRCS})
NGL_TARGET_LINK_LIBRARIES(picture_editing_test
LtsWidgets
)
# # LtsWidgetsTest
# SET(src_test
# tests/main.cpp
# tests/window.h
# tests/window.cpp
# )
# NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsWidgetsTest ${src_test})
# NGL_TARGET_LINK_LIBRARIES(LtsWidgetsTest
# LtsWidgets
# )
ENDIF()
+208
View File
@@ -0,0 +1,208 @@
#
# $Id$
# $HeadURL$
# $Author$
#
#
# Lts Widgets library
#
SET(ltswidgets_SRCS
#lts_abstract_table_model.h
#lts_checkable_table_model.h
lts_abstract_sequence_widget.cpp
lts_abstract_sequence_widget.h
lts_background_widget.cpp
lts_background_widget.h
lts_base.h
lts_base_layout_widget.cpp
lts_base_layout_widget.h
lts_column_filter_proxy_model.cpp
lts_column_filter_proxy_model.h
lts_combo_box.cpp
lts_combo_box.h
lts_selection_popup.cpp
lts_selection_popup.h
lts_drag_icon.cpp
lts_drag_icon.h
#lts_drop_handler.h
lts_elided_label.cpp
lts_elided_label.h
lts_events.cpp
lts_events.h
lts_frameless_dialog.cpp
lts_frameless_dialog.h
lts_grid_layout.cpp
lts_grid_layout.h
lts_height_plot_widget.cpp
lts_height_plot_widget.h
lts_message_dialog.cpp
lts_message_dialog.h
lts_message_dialog_helper.cpp
lts_message_dialog_helper.h
lts_profile_button.cpp
lts_profile_button.h
lts_progress_dialog_helper.cpp
lts_progress_dialog_helper.h
lts_splitter.cpp
lts_splitter.h
lts_splitter_handle.cpp
lts_splitter_handle.h
lts_tab_bar.cpp
lts_tab_bar.h
lts_tab_widget.cpp
lts_tab_widget.h
lts_tab_sequence_widget.cpp
lts_tab_sequence_widget.h
lts_tool_bar.cpp
lts_tool_bar.h
lts_tool_box.cpp
lts_tool_box.h
lts_tool_bar_area.cpp
lts_tool_bar_area.h
lts_dialog.cpp
lts_dialog.h
lts_dialog_helper.cpp
lts_dialog_helper.h
lts_dialog_button_box.cpp
lts_dialog_button_box.h
lts_operator_menu.cpp
lts_operator_menu.h
lts_image_editor.cpp
lts_image_editor.h
lts_led_widget.cpp
lts_led_widget.h
ui_requirement.cpp
ui_requirement.h
#lts_widgets_export.h
lts_picture_view.cpp
lts_picture_view.h
lts_global_wait_dialog.cpp
lts_global_wait_dialog.h
lts_wait_dialog.cpp
lts_wait_dialog.h
lts_splash.cpp
lts_splash.h
lts_info_dialog.cpp
lts_info_dialog.h
lts_spinbox.cpp
lts_spinbox.h
lts_multi_range_spinbox.cpp
lts_multi_range_spinbox.h
lts_text_editor.cpp
lts_text_editor.h
lts_text_edit_dialog.h
lts_text_edit_dialog.cpp
lts_range_slider.cpp
lts_range_slider.h
lts_notification_box.cpp
lts_notification_box.h
lts_msec_date_time_edit.cpp
lts_msec_date_time_edit.h
lts_bitfield_widget.cpp
lts_bitfield_widget.h
lts_overlay_loading.cpp
lts_angle_edit.cpp
lts_angle_edit.h
LtsImageCropper/lts_image_cropper.cpp
)
#if (TABLET_MODE)
# LIST(APPEND ltswidgets_SRCS
# lts_date_time_edit_touch.h
# lts_date_time_edit_touch.cpp
# )
# NGL_WRAP_UI(ltswidgets_SRCS
# lts_date_time_edit_touch.ui
# )
#else()
LIST(APPEND ltswidgets_SRCS
lts_date_time_edit.h
lts_date_time_edit.cpp
lts_date_time_edit_p.h
lts_date_time_edit_p.cpp
)
#endif()
NGL_WRAP_UI(ltswidgets_SRCS
lts_selection_popup.ui
lts_base_layout.ui
lts_tool_bar_area.ui
lts_overlay_loading.ui
lts_dialog_button_box.ui
lts_operator_menu.ui
lts_dialog_helper.ui
lts_info_dialog.ui
lts_text_editor.ui
lts_notification_box.ui
lts_angle_edit.ui
lts_date_time_edit_touch.ui
)
NGL_ADD_BIG_RESOURCES(ltswidgets_SRCS resources/lts_resources.qrc)
NGL_ADD_LIBRARY(WS TOOLSWS TOOLSSV LtsWidgets SHARED ${ltswidgets_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsWidgets
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsWidgets
PRIVATE
Qt5::Network # for QHostInfo - for portability
Qt5::Svg
ngltools
ngltranslator
TabBarTabStyle
LtsSessionData
LtsUtilsSettings
PUBLIC
LtsUtilsCore
Qt5::Core
Qt5::Widgets
Qt5::Gui
)
NGL_INSTALL_TARGET(LtsWidgets)
#
# Designer-Plugin
#
SET(ltswidgetsplugin_SRCS ltswidgetsplugin.cpp ltswidgetsplugin.json)
NGL_ADD_LIBRARY(WS ltswidgetsplugin MODULE ${ltswidgetsplugin_SRCS})
NGL_TARGET_LINK_LIBRARIES(ltswidgetsplugin LtsWidgets ltsCoordWidget LtsDataView LtsFileDialog LtsReportDisplay svcFlmCommon)
NGL_SET_TARGET_PROPERTIES(ltswidgetsplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/designer)
NGL_INSTALL_TARGET(ltswidgetsplugin DESTINATION lib/designer COMPONENT Devel)
IF(BUILD_TESTING)
# picture_editing_test
SET(pet_SRCS
tests/lts_picture_editing.cpp
tests/lts_picture_editing.h
tests/lts_picture_editing_main.cpp
)
NGL_WRAP_UI(pet_SRCS
tests/lts_picture_editing.ui
)
NGL_ADD_EXECUTABLE(WS NO_TRANSLATION picture_editing_test ${pet_SRCS})
NGL_TARGET_LINK_LIBRARIES(picture_editing_test
LtsWidgets
)
# # LtsWidgetsTest
# SET(src_test
# tests/main.cpp
# tests/window.h
# tests/window.cpp
# )
# NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsWidgetsTest ${src_test})
# NGL_TARGET_LINK_LIBRARIES(LtsWidgetsTest
# LtsWidgets
# )
ENDIF()
+137
View File
@@ -0,0 +1,137 @@
/*
* @RDE_HEADER@
* graphicsview widget to resize qpixmaps/qimages with mouse drag and mwheel
* $Id$
* $HeadURL$
* $Author$
* Created by Jens Heilmann, SLTE, 2020-10-14
*/
#include "lts_image_cropper.h"
#include <QGraphicsItem>
#include <QMouseEvent>
#include <QApplication>
namespace LtsWidgets
{
LtsImageCropper::LtsImageCropper(QWidget *parent, const QSize &size, const QPixmap &pixmap)
: QGraphicsView(parent)
, m_scene(new QGraphicsScene(this))
, m_maxScale(2.0f)
, m_multiplier(1.0f)
{
if(!size.isNull())
setFixedSize(size);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameStyle(QFrame::NoFrame);
setDragMode(QGraphicsView::ScrollHandDrag);
setTransformationAnchor(QGraphicsView::AnchorViewCenter);
m_scene->setBackgroundBrush(Qt::Dense6Pattern);
setScene(m_scene);
setPixmap(pixmap);
}
LtsImageCropper::~LtsImageCropper()
{
delete m_pixmapItem;
}
void LtsImageCropper::setPixmap(const QPixmap &pixmap)
{
if(pixmap.isNull())
return;
delete m_pixmapItem; // delete previous pixmapitem
m_pixmapItem = new QGraphicsPixmapItem(pixmap);
m_scene->addItem(m_pixmapItem);
setTransform(QTransform()); // set a fresh transformation matrix
m_currentZoom = 1.0; // reset current zoom
//check if image size is min view, if not scale it
double sx = static_cast<double>(size().height()) / pixmap.height();
double sy = static_cast<double>(size().width()) / pixmap.width();
if(sx > 1 )
scale(sx,sx);
if(sy > 1)
scale(sy,sy);
// calculate min scale from qMin(width,height);
double ratioWidth = 1.0 * size().width() / pixmap.width();
double ratioHeight = 1.0 * size().height() / pixmap.height();
m_minScale = qMax(ratioHeight,ratioWidth);
calculateScaleStep();
// change scene boundaries to image rectangle and center scene on image center
m_scene->setSceneRect(pixmap.rect());
centerOn(m_scene->sceneRect().center());
}
void LtsImageCropper::setImage(const QImage &image)
{
setPixmap(QPixmap::fromImage(image));
}
void LtsImageCropper::setMultiplier(double multiplier)
{
m_multiplier = multiplier;
calculateScaleStep();
}
void LtsImageCropper::calculateScaleStep()
{
m_stepScale = (m_maxScale - m_minScale) / 100.0;
}
QImage LtsImageCropper::image()
{
return grab().toImage();
}
void LtsImageCropper::wheelEvent(QWheelEvent *event)
{
if(!m_pixmapItem)
return;
auto value = event->pixelDelta();
if(value.isNull())
value = event->angleDelta() / 15.0;
// calculate scrollZoom value to be added to the current zoom
double scrollZoom = m_stepScale * value.y() * m_multiplier / 8.0;
m_currentZoom += scrollZoom;
if(m_currentZoom > m_maxScale)
m_currentZoom = m_maxScale;
else if (m_currentZoom < m_minScale)
m_currentZoom = m_minScale;
// scale the view
setTransform(QTransform(m_currentZoom, transform().m12(), transform().m13(),
transform().m21(), m_currentZoom, transform().m23(),
transform().m31(), transform().m32(), 1));
}
void LtsImageCropper::mousePressEvent(QMouseEvent *event)
{
QApplication::setOverrideCursor(Qt::DragMoveCursor);
update();
QGraphicsView::mousePressEvent(event);
}
void LtsImageCropper::mouseReleaseEvent(QMouseEvent *event)
{
QApplication::restoreOverrideCursor();
update();
QGraphicsView::mouseReleaseEvent(event);
}
}
+108
View File
@@ -0,0 +1,108 @@
/*
* @RDE_HEADER@
* graphicsview widget to resize qpixmaps/qimages with mouse drag and mwheel
* $Id$
* $HeadURL$
* $Author$
* Created by Jens Heilmann, SLTE, 2020-10-14
*/
#pragma once
#include "lts_widgets_export.h"
#include <QGraphicsView>
namespace LtsWidgets
{
/**
* @brief The ImageCropper class provides a graphicsview widget to crop images with mouse drag and mwheel scroll.
* avatar() returns a pixmap of the current view (the cropped image).
*/
class LTSWIDGETS_EXPORT LtsImageCropper : public QGraphicsView
{
Q_OBJECT
Q_PROPERTY(QPixmap pixMap READ pixmap WRITE setPixmap)
Q_PROPERTY(double multiplier READ multiplier WRITE setMultiplier)
Q_PROPERTY(double maxZoom READ maxZoom WRITE setMaxZoom)
public:
/**
* @brief ImageCropper
* @param parent
* @param size specifies the size of the graphicsview widget, aswell as the size of the cropped image.
* @param image to crop
*/
LtsImageCropper(QWidget *parent = nullptr, const QSize &size = {} , const QPixmap &pixmap = {});
~LtsImageCropper();
/**
* @brief setter for image
* @param image
*/
void setPixmap(const QPixmap &pixmap);
/**
* @brief setter for image
* @param image
*/
void setImage(const QImage &image);
/**
* @brief set the zoom mutliplier for mwheel scaling e.g. 1.03.
* @param multiplier
*/
void setMultiplier(double multiplier);
/**
* @brief set the maximum zoom in scale e.g. 2.0 = 200% zoom in.
* @param maxZoom
*/
void setMaxZoom(double maxZoom) { m_maxScale = maxZoom; }
/**
* @brief returns the current graphicsview view as a pixmap.
* @return qpixmap
*/
QPixmap pixmap() { return grab(); }
/**
* @brief returns the current graphicsview view as qimage.
* @return qimage
*/
QImage image();
/**
* @brief returns the current multiplier
* @return double
*/
double multiplier() const { return m_multiplier; }
/**
* @brief returns the current max zoom value
* @return double
*/
double maxZoom() const { return m_maxScale; }
private:
QGraphicsScene *m_scene = nullptr;
QGraphicsPixmapItem *m_pixmapItem = nullptr;
double m_currentZoom;
double m_minScale;
double m_maxScale;
double m_stepScale;
double m_multiplier;
void calculateScaleStep();
protected:
void wheelEvent(QWheelEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
};
} // namespace LtsWidgets
+126
View File
@@ -0,0 +1,126 @@
/**
* @RDE_HEADER@
* ABC widget for creating custom views of sequences
* $Id$
* $HeadURL$
* $Author$
* Created by Verena Eberlein, SPMO, 2023-08-03
*/
#include "lts_abstract_sequence_widget.h"
#include <QToolButton>
namespace LtsWidgets
{
LtsAbstractSequenceWidget::LtsAbstractSequenceWidget(QWidget *parent, const bool &readOnly)
: QGroupBox(parent)
, m_readOnly(readOnly)
, m_pbAddMember(new QToolButton)
{
m_pbAddMember->setIcon(QIcon(QLatin1String(":/icons/png/add.png")));
connect(m_pbAddMember, &QToolButton::clicked, this, &LtsAbstractSequenceWidget::addMember);
}
LtsAbstractSequenceWidget::~LtsAbstractSequenceWidget()
{
}
void LtsAbstractSequenceWidget::addWidget(QWidget* widget, bool isReadOnly, bool isButtonVisible)
{
addMemberWidget(widget, isReadOnly, isButtonVisible);
m_pbAddMember->setDisabled(isReadOnly);
m_pbAddMember->setVisible(isButtonVisible);
}
int LtsAbstractSequenceWidget::getMemberlistSize(){
return m_memberList.size();
}
void LtsAbstractSequenceWidget::getValue(QWidget *data,
const QString &findObjectName) const
{
if(!findChild<QWidget *>(findObjectName))
return;
data = findChild<QWidget *>(findObjectName);
}
void LtsAbstractSequenceWidget::setMemberCount(int count)
{
QWidget *child = nullptr;
do
{
child = findChild<QWidget *>(getObjectName(++count));
delete child;
}
while (child);
m_memberList.removeAll(nullptr);
}
uint32_t LtsAbstractSequenceWidget::getMemberCount() const
{
int count = 0;
QWidget *child = findChild<QWidget *>(getObjectName(count+1));
while(child)
{
count++;
child = findChild<QWidget *>(getObjectName(count+1));
}
return count;
}
QToolButton *LtsAbstractSequenceWidget::pbAddMember() const
{
return m_pbAddMember;
}
QString LtsAbstractSequenceWidget::getObjectName(int index) const
{
return QString::number(index);
}
void LtsAbstractSequenceWidget::addMemberWidget(QWidget *member, bool isReadOnly, bool isButtonVisible)
{
QToolButton *button = new QToolButton();
button->setIcon(QIcon(QLatin1String(":/icons/png/remove.png")));
button->setDisabled(isReadOnly);
connect(button, &QToolButton::clicked, member, &QWidget::deleteLater);
connect(member, &QWidget::destroyed, this, &LtsAbstractSequenceWidget::deleteMemberWidget);
connect(member, &QWidget::destroyed, button, &QToolButton::deleteLater);
m_memberList.push_back(member);
setupMemberWidget(member, button, member->objectName(), isButtonVisible);
}
void LtsAbstractSequenceWidget::deleteMemberWidget(QObject *member)
{
QWidget *w = qobject_cast<QWidget*>(member);
if(!w)
return;
for(auto child : w->children()) // necessary?
child->deleteLater();
m_memberList.removeAll(w);
for(int i=0; i<m_memberList.size(); i++)
{
m_memberList[i]->setObjectName(getObjectName(i + 1));
for(auto child : m_memberList[i]->children())
{
if(child->objectName().isEmpty())
continue;
QString childName = child->objectName();
QString newId = QString::number(i + 1);
child->setObjectName(childName.replace(QRegExp(QLatin1String(".([0-9]*)")), newId));
}
}
}
} // namespace LtsWidgets
+126
View File
@@ -0,0 +1,126 @@
#ifndef LTS_ABSTRACT_SEQUENCE_WIDGET_H
#define LTS_ABSTRACT_SEQUENCE_WIDGET_H
/**
* @RDE_HEADER@
* ABC for creating custom views of dds sequences
* $Id$
* $HeadURL$
* $Author$
* Created by Verena Eberlein, SPMO, 2023-08-03
*/
#pragma once
#include <QGroupBox>
#include <QList>
#include "lts_widgets_export.h"
class QToolButton;
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsAbstractSequenceWidget : public QGroupBox
{
Q_OBJECT
public:
/**
* @brief LtsAbstractSequenceWidget inits pbAddMember and adds logic to it.
* @attention pbAddMember must be inserted into a layout
* @param readOnly if read only, disable all buttons on creation
* @param name
* @param contentType
* @param parent
*/
explicit LtsAbstractSequenceWidget(QWidget *parent = nullptr, const bool &readOnly = false);
~LtsAbstractSequenceWidget();
/**
* @brief adds a given QWidget
* @param widget
*/
virtual void addWidget(QWidget* widget, bool isReadOnly, bool isButtonVisible = true);
/**
* @brief getValue get widget by objectname
* @param contentType
* @param findObjectName
*/
virtual void getValue(QWidget *contentType,
const QString &findObjectName) const;
/**
* @brief setMemberCount
* @param count
*/
virtual void setMemberCount(int count);
/**
* @brief getMemberCount by objectname
* @return count
*/
virtual uint32_t getMemberCount() const;
/**
* @brief get the current memberlist size
* @return count
*/
virtual int getMemberlistSize();
protected:
/**
* @brief pbAddMember
* @return
*/
virtual QToolButton *pbAddMember() const;
/**
* @brief getObjectName generates a valid object name given by index
* @param index
* @return
*/
virtual QString getObjectName(int index) const;
/**
* @brief setupMemberWidget place child and button widget into a layout
* @param child
* @param pbChild
* @param objectName
*/
virtual void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName, bool isButtonVisible = true) = 0;
signals:
/**
* @brief signal is send when ToolButton is pressed so we can add a member widget via addWidget
*/
void addMember();
private slots:
/**
* @brief addMemberWidget create a new button and connect to child
* @param member new child
*/
virtual void addMemberWidget(QWidget *member, bool isReadOnly, bool isButtonVisible = true);
/**
* @brief deleteMemberWidget clean up child and reindex other children objectnames
* @param member deleted child
*/
virtual void deleteMemberWidget(QObject *member);
private:
bool m_readOnly;
QToolButton *m_pbAddMember = nullptr;
QList<QWidget*> m_memberList;
};
}
#endif // LTS_ABSTRACT_SEQUENCE_WIDGET_H
+293
View File
@@ -0,0 +1,293 @@
/**
* @RDE_HEADER@
*
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, SLSE, 2017-06-20
*/
#pragma once
#include "lts_base.h"
#include <LtsUtilsCore/data_modification_enum.h>
#include <QVector>
#include <QAbstractTableModel>
#include <memory>
#include <algorithm>
namespace LtsWidgets
{
template<class T>
class LtsAbstractTableModel : public QAbstractTableModel
{
public:
LtsAbstractTableModel(const QVector<QString> &header, QObject *parent)
: QAbstractTableModel(parent)
, m_vecColumns(header)
{}
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section>= 0 && section < m_vecColumns.size())
return m_vecColumns[section];
return QAbstractTableModel::headerData(section, orientation, role);
}
bool isEmpty(const QModelIndex &parent = QModelIndex())
{
return parent.isValid() ? true : m_vecData.isEmpty();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
return parent.isValid() ? 0 : m_vecData.size();
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
return parent.isValid() ? 0 : m_vecColumns.size();
}
void clear()
{
beginResetModel();
m_vecData.clear();
endResetModel();
}
bool indexIsValid(const QModelIndex &idx) const
{
if (!idx.isValid())
return false;
if (idx.row() < 0 || idx.row() >= m_vecData.size())
return false;
return true;
}
virtual bool removeRow(int row)
{
return removeRows(row, 1);
}
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
if (parent.isValid())
return false;
if (row < 0 || row >= m_vecData.size())
return false;
int range = std::min(row + count, m_vecData.size());
beginRemoveRows(QModelIndex(), row, range - 1);
m_vecData.erase(m_vecData.begin() + row, m_vecData.begin() + range);
endRemoveRows();
return true;
}
virtual bool insertRow(const T &item)
{
if (isFiltered(item, DataModification::eAdded) || isDeletedInternal(item))
return false;
beginInsertRows(QModelIndex(), m_vecData.size(), m_vecData.size());
m_vecData.append(item);
endInsertRows();
return true;
}
template <typename CONT>
void insertRows(const CONT &items)
{
if (items.isEmpty())
return;
const int cntBefore = m_vecData.size();
for (const auto &item : items)
{
if (isFiltered(item, DataModification::eAdded) || isDeletedInternal(item))
continue;
m_vecData += item;
}
const int cntAfter = m_vecData.size();
if (cntBefore == cntAfter)
return;
beginInsertRows(QModelIndex(), cntBefore, cntAfter - 1);
endInsertRows();
}
void setData(const QVector<T> &vecData)
{
clear();
insertRows(vecData);
}
/*
* Emits Qt's signal dataChanged for the whole model data
*/
void update()
{
if (!m_vecData.isEmpty())
emit dataChanged(index(0, 0), index(m_vecData.size() - 1, columnCount() - 1));
}
T getRowData(int row) const
{
if (row < 0 || row >= m_vecData.size())
return T();
return m_vecData.at(row);
}
bool setRowData(int row, const T &data)
{
if (row < 0 || row >= m_vecData.size())
return false;
m_vecData[row] = data;
emit dataChanged(index(row, 0), index(row, columnCount() - 1));
return true;
}
QModelIndex findIndexForId(int id, int column = 0) const
{
const int row = findRowForId(id);
if (row < 0)
return QModelIndex();
return index(row, column);
}
int getId(const QModelIndex &mi) const
{
if (!indexIsValid(mi))
return -1;
return getIdInternal(m_vecData.at(mi.row()));
}
protected:
template <class C>
class HasId
{
template <class CLS>
static std::true_type testSignature(const int &(CLS::*)() const);
template <class CLS>
static std::true_type testSignature(int (CLS::*)() const);
template <class CLS>
static decltype(testSignature(&CLS::id)) test(std::nullptr_t);
template <class CLS>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
// enable getId() if there is a function F::id
template <typename F = T, typename std::enable_if<HasId<F>::value, int>::type = 0>
int getIdInternal(const F &data) const
{
return data.id();
}
// fallback, should assert since it does not return a valid value
// static_assert is not possible since virtual void onDataChanged() is instantiated (due to virtual)
virtual int getId(const T &data) const
{
Q_ASSERT(false);
Q_UNUSED(data);
return -1;
}
template < typename F = T, typename std::enable_if < !HasId<F>::value, int>::type = 0>
int getIdInternal(const F &data) const
{
return getId(data);
}
template <class C>
class HasIsDeleted
{
template <class CLS>
static std::true_type testSignature(const bool &(CLS::*)() const);
template <class CLS>
static std::true_type testSignature(bool (CLS::*)() const);
template <class CLS>
static decltype(testSignature(&CLS::isDeleted)) test(std::nullptr_t);
template <class CLS>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
// if data.isDeleted() is available - return it's value
template <typename F = T, typename std::enable_if<HasIsDeleted<F>::value, int>::type = 0>
bool isDeletedInternal(const F &data) const
{
return data.isDeleted();
}
// fallback
virtual int isDeleted(const T &data) const
{
Q_UNUSED(data);
return false;
}
template < typename F = T, typename std::enable_if < !HasIsDeleted<F>::value, int>::type = 0>
bool isDeletedInternal(const F &data) const
{
return isDeleted(data);
}
// virtual to allow overriding this function for special cases
virtual bool isFiltered(const T &data, DataModification modType) const
{
Q_UNUSED(data);
Q_UNUSED(modType);
return false;
}
// virtual to allow overriding this function for special cases
virtual bool onDataChanged(const T &data, DataModification modType)
{
if (isFiltered(data, modType))
return false;
const int id = getIdInternal(data);
const int row = findRowForId(id);
// isDeletedInternal -> T::isDeleted() == true
if (modType == DataModification::eRemoved || isDeletedInternal(data))
{
if (row >= 0)
return removeRow(row);
return false;
}
if (modType == DataModification::eAdded && row < 0)
return insertRow(data);
return setRowData(row, data);
}
int findRowForId(int id) const
{
for (int i = 0; i < m_vecData.size(); ++i)
{
if (getIdInternal(m_vecData.at(i)) == id)
return i;
}
return -1;
}
QVector<T> m_vecData;
QVector<QString> m_vecColumns;
};
}
+281
View File
@@ -0,0 +1,281 @@
/**
* LtsAngleEdit
* $Id$
* $HeadURL$
* $Author$
*
* Created by Steve Kemayou, ESZS, 2024-10-16
*/
#include "lts_angle_edit.h"
#include "ui_lts_angle_edit.h"
#include <cmath>
namespace LtsWidgets
{
// some helper functions
inline constexpr double pi() { return M_PI; }
inline constexpr double deg2rad() { return pi() / 180.; }
inline constexpr double rad2deg() { return 1. / deg2rad(); }
constexpr int maxAngleDeg = 360;
constexpr int maxAngleMil = 6400;
constexpr std::tuple<int,int,int,int> c_limDeg (0, maxAngleDeg,0, 10); // angleMinVal, angleMaxVal, decimal place, spinboxStep
constexpr std::tuple<int,int,int, int> c_limMil(0, maxAngleMil,0, 50);
constexpr std::tuple<int,int,int, int> c_limRad(-99999, 99999, 3, 1);
inline constexpr double mil2Deg(const double &mil)
{
return maxAngleDeg * mil / maxAngleMil;
}
inline constexpr double deg2Mil(const double &angleDeg)
{
return maxAngleMil * angleDeg / maxAngleDeg;
}
inline constexpr double deg2Rad(const double &angleDeg)
{
return angleDeg * deg2rad();
}
inline constexpr double rad2Degree(const double &angleRad)
{
return angleRad * 180/pi();
}
inline constexpr double mil2Rad(const double &mil)
{
return deg2Rad(mil2Deg(mil));
}
inline constexpr double rad2Mil(const double &angleRad)
{
return deg2Mil(rad2Degree(angleRad));
}
/*!
* \brief LtsAngleEdit::LtsAngleEdit
* \param parent
*/
LtsAngleEdit::LtsAngleEdit(QWidget *parent) :
QWidget(parent),
m_ui(new Ui_LtsAngleEdit)
{
m_ui->setupUi(this);
}
LtsAngleEdit::~LtsAngleEdit()
{
delete m_ui;
}
/*!
* \brief LtsAngleEdit::initAngleEditWidget
*/
void LtsAngleEdit::initAngleEditWidget(const unsigned int sboxCount)
{
// set Limits
m_dboxLimitMap.insert(AngleUnitType::Deg, c_limDeg);
m_dboxLimitMap.insert(AngleUnitType::Mil, c_limMil);
m_dboxLimitMap.insert(AngleUnitType::Rad, c_limRad);
m_hiddenUnits.clear();
m_hiddenUnits.insert((int)AngleUnitType::Rad); // default hid rad unit
// add new dboxes
for(unsigned int i=0; i<sboxCount; ++i)
{
QSharedPointer<QDoubleSpinBox> spinbox = QSharedPointer<QDoubleSpinBox>(new QDoubleSpinBox(this) );
spinbox->setValue(0);
spinbox->setAlignment(Qt::AlignRight);
spinbox->setMinimumWidth(91);
// set object name
spinbox.data()->setObjectName(spinBoxName+QString::number(m_sBoxMap.count()));
m_ui->hLayoutSpinbox->addWidget(spinbox.data(), 0);
m_sBoxMap.insert(m_sBoxMap.count(), spinbox);
}
m_ui->hLayoutSpinbox->setContentsMargins(1,1,1,1);
// set combobox units
connect(m_ui->cBoxAngleUnit, qOverload<int>(&QComboBox::currentIndexChanged),
this, &LtsAngleEdit::onCBoxAngleUnitChanged);
setCboxUnit();
// set default unit
}
/*!
* \brief LtsAngleEdit::setValue
* \param value
* \param dboxIndex
*/
void LtsAngleEdit::setValue(const double value, unsigned int dboxIndex)
{
if ((int)dboxIndex > m_sBoxMap.count())
return;
auto spinbox = m_sBoxMap.value(dboxIndex);
spinbox->setValue(value);
}
/*!
* \brief LtsAngleEdit::setValue
* \param value
* \param valueUnit
* \param dboxIndex
*/
void LtsAngleEdit::setValue(const double value, AngleUnitType valueUnit, unsigned int dboxIndex)
{
setValue(getAngleConverted(value, getAngleCurrentUnit(), valueUnit),dboxIndex);
}
void LtsAngleEdit::hideUnits(const QVector<AngleUnitType> &list)
{
for(auto u : list)
m_hiddenUnits.insert((int)u);
setCboxUnit();
}
void LtsAngleEdit::showUnits(const QVector<AngleUnitType> &list)
{
for (const auto &u : list)
m_hiddenUnits.remove((int)u);
setCboxUnit();
}
/*!
* \brief LtsAngleEdit::setCboxUnit
*/
void LtsAngleEdit::setCboxUnit()
{
m_ui->cBoxAngleUnit->clear();
const std::vector<AngleUnitType> allUnits = {AngleUnitType::Deg, AngleUnitType::Mil, AngleUnitType::Rad};
for(const auto &u : allUnits)
{
if (m_hiddenUnits.contains((int)u))
continue;
m_ui->cBoxAngleUnit->insertItem(static_cast<int>(u), toString(u),static_cast<int>(u));
}
setAngleUnit(AngleUnitType::Deg);
}
/*!
* \brief LtsAngleEdit::setValue
* \param value
* \param valueUnit
*/
void LtsAngleEdit::setValue(const double value, AngleUnitType valueUnit)
{
// get current unit of given angle and convert it to current defined in combobox unit
setValue(getAngleConverted(value, getAngleCurrentUnit(), valueUnit),0);
}
/*!
* \brief LtsAngleEdit::setAngleUnit
* \param type
*/
void LtsAngleEdit::setAngleUnit(const AngleUnitType &type)
{
m_ui->cBoxAngleUnit->setCurrentIndex(static_cast<int>(type));
}
/*!
* \brief LtsAngleEdit::getCurrentAngleUnit
* \return
*/
LtsAngleEdit::AngleUnitType LtsAngleEdit::getAngleCurrentUnit() const
{
return static_cast<AngleUnitType>(m_ui->cBoxAngleUnit->currentIndex());
}
/*!
* \brief LtsAngleEdit::getAngleValue
* \param dboxIndex default index value is 0; also when more than one dbox is created
* \return
*/
double LtsAngleEdit::getAngle(unsigned int sboxIndex) const
{
if ((int)sboxIndex > m_sBoxMap.count())
return 0;
auto spinbox = m_sBoxMap.value(sboxIndex);
return spinbox->value();
}
/*!
* \brief LtsAngleEdit::toString
* \param t
* \return
*/
QString LtsAngleEdit::toString(LtsAngleEdit::AngleUnitType t)
{
switch(t)
{
case AngleUnitType::Deg:
return tr("Degree");
case AngleUnitType::Mil:
return tr("Mil");
case AngleUnitType::Rad:
return tr("Radian");
default:
return tr("");
}
return tr("");
}
void LtsAngleEdit::onCBoxAngleUnitChanged()
{
AngleUnitType newUnit = static_cast<AngleUnitType>(m_ui->cBoxAngleUnit->currentData().toInt());
for(auto dbox : qAsConst(m_sBoxMap))
{
const double value = getAngleConverted(dbox->value(), newUnit, m_currAngleUnit);
auto mapVal = m_dboxLimitMap.value(newUnit);
dbox->setMinimum(std::get<LimTuple::AngleMin>(mapVal));
dbox->setMaximum(std::get<LimTuple::AngleMax>(mapVal));
dbox->setDecimals(std::get<LimTuple::DecimalPlace>(mapVal));
dbox->setSingleStep(std::get<LimTuple::SingleStep>(mapVal));
dbox->setValue(value);
}
m_currAngleUnit = newUnit;
}
/*!
* \brief LtsAngleEdit::convertAngleValue
* \param angle
* \param newAngleUnit
* \param oldAngleUnit
* \return
*/
double LtsAngleEdit::getAngleConverted(const double angle, LtsAngleEdit::AngleUnitType newAngleUnit, LtsAngleEdit::AngleUnitType oldAngleUnit)
{
double convertedAngle = angle;
if (oldAngleUnit == AngleUnitType::Deg && newAngleUnit == AngleUnitType::Mil )
convertedAngle = deg2Mil(angle);
else if (oldAngleUnit == AngleUnitType::Deg && newAngleUnit == AngleUnitType::Rad )
convertedAngle = deg2Rad(angle);
else if (oldAngleUnit == AngleUnitType::Mil && newAngleUnit == AngleUnitType::Deg )
convertedAngle = mil2Deg(angle);
else if (oldAngleUnit == AngleUnitType::Mil && newAngleUnit == AngleUnitType::Rad )
convertedAngle = mil2Rad(angle);
else if (oldAngleUnit == AngleUnitType::Rad && newAngleUnit == AngleUnitType::Deg )
convertedAngle = rad2Degree(angle);
else if (oldAngleUnit == AngleUnitType::Rad && newAngleUnit == AngleUnitType::Mil )
convertedAngle = rad2Mil(angle);
return convertedAngle;
}
void LtsAngleEdit::setEnable(int sbIndex, bool enabled)
{
if ((int)sbIndex > m_sBoxMap.count())
return;
m_sBoxMap.value(sbIndex)->setEnabled(enabled);
}
void LtsAngleEdit::showSpinbox(int sbIndex, bool visible)
{
if ((int)sbIndex > m_sBoxMap.count())
return;
m_sBoxMap.value(sbIndex)->setVisible(visible);
}
}// namespace LtsAngleEdit
+77
View File
@@ -0,0 +1,77 @@
/**
* LtsAngleEdit
* $Id$
* $HeadURL$
* $Author$
*
* Created by Steve Kemayou, ESZS, 2024-10-16
*/
#pragma once
#include <QWidget>
#include <QDoubleSpinBox>
#include <QSet>
#include <LtsWidgets/lts_widgets_export.h>
class Ui_LtsAngleEdit;
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsAngleEdit : public QWidget
{
Q_OBJECT
public:
explicit LtsAngleEdit(QWidget *parent = nullptr);
~LtsAngleEdit();
enum class AngleUnitType : unsigned int
{
Deg = 0,
Mil = 1,
Rad = 2
};
Q_ENUM(AngleUnitType)
enum LimTuple : int
{
AngleMin,
AngleMax,
DecimalPlace,
SingleStep
};
const QString spinBoxName = QStringLiteral("sboxAngleValue");
QString toString(AngleUnitType t);
void setAngleUnit(const AngleUnitType &type);
void setValue(const double value, unsigned int dboxIndex=0); /// set value of spinbox with index. index start from 0. Counting from Left to right.
void setValue(const double value, AngleUnitType valueUnit); /// set value with current unit of given angle and convert it to current defined unit in combobox unit
void setValue(const double value, AngleUnitType valueUnit, unsigned int dboxIndex);
void hideUnits(const QVector<AngleUnitType> &list); /// list of units that will not be displayed in cbox
void showUnits(const QVector<AngleUnitType> &list); /// list of units to be displayed in cbox
LtsAngleEdit::AngleUnitType getAngleCurrentUnit() const; /// get cbox current unit
void initAngleEditWidget(const unsigned int sboxCount); /// this method initialize the angle edit widget. sboxCount give number of spinbox to be created
double getAngle(unsigned int sboxIndex = 0) const; /// sboxIndex default index value is 0; also when more than one dbox is created
double getAngleConverted(const double angle, LtsAngleEdit::AngleUnitType newAngleUnit, LtsAngleEdit::AngleUnitType oldAngleUnit);
void setEnable(int sbIndex, bool enabled); /// set spinbox at index enabled or disabled
void showSpinbox(int sbIndex, bool visible); /// show spinBox of specific index. Indexes start from 0.
protected:
void setCboxUnit(); /// fill combobox with units
private Q_SLOTS:
void onCBoxAngleUnitChanged();
private:
Ui_LtsAngleEdit *m_ui;
QMap<int, QSharedPointer<QDoubleSpinBox>> m_sBoxMap; /// Map<sbindex, sbWidget>
QMap<AngleUnitType, std::tuple<int,int,int,int>> m_dboxLimitMap; /// minAngle, maxAngle, decimal place, single step
AngleUnitType m_currAngleUnit;
QSet<int> m_hiddenUnits;
};
}//namespace LtsAngleEdit
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsAngleEdit</class>
<widget class="QWidget" name="LtsAngleEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>107</width>
<height>34</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutSpinbox"/>
</item>
<item>
<widget class="QComboBox" name="cBoxAngleUnit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+143
View File
@@ -0,0 +1,143 @@
/**
* @RDE_HEADER@
* Background Widget that covers a monitor with the project image
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2016-05-05
*/
#include "lts_background_widget.h"
#include <QPaintEvent>
#include <QPainter>
#include <QDebug>
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
#endif
namespace LtsWidgets
{
LtsBackgroundWidget::LtsBackgroundWidget(QWidget *parent)
: QDialog(parent)
, m_isUltraWide(false)
{
setMouseTracking(true); // register mouse movement
setWindowFlags(windowFlags() | Qt::FramelessWindowHint /*| Qt::WindowStaysOnBottomHint*/);
setWindowTitle(QStringLiteral("Splash"));
setBackground();
}
void LtsBackgroundWidget::setBackground()
{
static const auto backgroundFiles =
{
QStringLiteral(":/project/background.png"),
QStringLiteral(":/project/background.jpg"),
};
static const auto backgroundFilesWide =
{
QStringLiteral(":/project/background_wide.png"),
QStringLiteral(":/project/background_wide.jpg"),
};
m_pixmap = QPixmap();
for (const QString &f : m_isUltraWide ? backgroundFilesWide : backgroundFiles)
{
if (!QFile::exists(f))
continue;
m_pixmap = QPixmap(f);
if (m_pixmap.isNull())
continue;
break;
}
if (m_pixmap.isNull())
{
if (m_isUltraWide)
{
m_isUltraWide = false;
setBackground();
}
else
{
qWarning() << "BackgroundWidget::c_tor: file missing or is not a valid image ':/project/background.png'";
}
}
}
void LtsBackgroundWidget::setUltraWide(bool set)
{
if (set == m_isUltraWide) // do not set / unset ultra wide if it's already set to specified state
return;
m_isUltraWide = set;
setBackground();
update(); // force redraw on next event loop
}
void LtsBackgroundWidget::resizeEvent(QResizeEvent *e)
{
setUltraWide(width() > 3000);
QDialog::resizeEvent(e);
}
void LtsBackgroundWidget::paintEvent(QPaintEvent *e)
{
if (!m_pixmap.isNull())
{
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.drawPixmap(0, 0, width(), height(), m_pixmap); // draw project background on login splash
}
QDialog::paintEvent(e);
}
bool LtsBackgroundWidget::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress || // respect typing into textfields
e->type() == QEvent::MouseButtonPress ||
e->type() == QEvent::MouseButtonRelease ||
e->type() == QEvent::MouseMove)
{
//qWarning() << "BGW operator action!";
emit signalOperatorAction();
e->ignore();
return true;
}
return QDialog::event(e);
}
void LtsBackgroundWidget::showFullScreen()
{
//qWarning() << "show fullscreen !";
#ifdef Q_OS_WIN
winId(); // make sure windowHandle() is not a nullptr
QWindowsWindowFunctions::setHasBorderInFullScreen(windowHandle(), true);
#endif
QDialog::showFullScreen();
raise(); // as this is a widget for the background, it has to be raised, but only once at startup
}
void LtsBackgroundWidget::closeEvent(QCloseEvent *e)
{
#ifdef DEVEL
deleteLater();
#else
e->ignore();
#endif
}
} // namespace LtsWidgets
+60
View File
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* Background Widget that covers a monitor with the project image
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2016-05-05
*/
#pragma once
#include "lts_widgets_export.h"
#include <QDialog>
class QPixmap;
class QEvent;
namespace LtsWidgets
{
/**
* @brief The LtsBackgroundWidget class creates a fullscreen Widget that covers a monitor with the project image (used for payloads in opApp)
*/
class LTSWIDGETS_EXPORT LtsBackgroundWidget : public QDialog
{
Q_OBJECT
public:
explicit LtsBackgroundWidget(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *e) override;
void closeEvent(QCloseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
bool event(QEvent *e) override;
private:
void setBackground();
void setUltraWide(bool set = true);
signals:
/**
* @brief signalOperatorAction is emitted when the operator moves/clickes the cursor or enters some letters - for example, show window again after it's hidden (screen saver effect)
*/
void signalOperatorAction();
public slots:
/**
* @brief showFullScreen shows the widget and raises it. Otherwise the widget would be far in the background (and wihtout a show not shown)
*/
void showFullScreen();
private:
QPixmap m_pixmap;
bool m_isUltraWide;
};
}
+281
View File
@@ -0,0 +1,281 @@
/**
* @RDE_HEADER@
* Enumerations for ltsbase
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_widgets_export.h"
#include <LtsUtilsCore/lts_translatable_identifier.h>
#include <QString>
#include <QString>
#include <QIcon>
#include <QScrollArea>
#include <QScroller>
#include <QAbstractItemView>
#include <functional>
namespace LtsWidgets
{
class LtsDialog;
inline QString readableDuration(qint64 msecs, bool foldDays = true)
{
if (msecs == 0)
return QObject::tr("---");
// If there's a subsecond value it's less than a minute, then show decimals, Otherwise ignore.
if (msecs % 1000 != 0 && msecs < 1000 * 60)
return QObject::tr("%1 seconds").arg(QLocale().toString(msecs / 1000.0, 'f', 3));
qint64 secs = msecs / 1000;
qint64 mins = secs / 60;
qint64 hours = mins / 60;
qint64 days = hours / 24;
if (foldDays)
days = 0;
else
hours = hours % 24;
mins = mins % 60;
secs = secs % 60;
// If only one is not equal 0, return long readable text:
if (!secs && !mins && !hours)
return QObject::tr("%n day(s)", "", days);
if (!secs && !mins && !days)
return QObject::tr("%n hour(s)", "", hours);
if (!secs && !hours && !days)
return QObject::tr("%n minute(s)", "", mins);
if (!mins && !hours && !days)
return QObject::tr("%n second(s)", "", secs);
QString strSecs = QObject::tr("%1s").arg(secs, 2, 10, QLatin1Char('0'));
QString strMins = QObject::tr("%1m").arg(mins, 2, 10, QLatin1Char('0'));
QString strHours = QObject::tr("%1h").arg(hours, 2, 10, QLatin1Char('0'));
QString strDays = QObject::tr("%1d").arg(days);
QString retVal;
if (days)
retVal += strDays + QLatin1Char(' ');
if (days || hours)
retVal.append(strHours);
if (mins || hours || days)
retVal.append(strMins);
if (secs)
retVal.append(strSecs);
return retVal;
}
inline void addTouchScroll(QWidget *widget)
{
if (!widget)
return;
widget->setAttribute(Qt::WA_AcceptTouchEvents);
{
QScrollArea *sca = qobject_cast<QScrollArea *>(widget);
if (sca)
{
QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture);
return;
}
}
{
QAbstractItemView *aiv = qobject_cast<QAbstractItemView *>(widget);
if (aiv)
{
QScroller::grabGesture(aiv->viewport(), QScroller::LeftMouseButtonGesture);
aiv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
aiv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
return;
}
}
}
enum class ButtonSize
{
eDefault = 40,
// eTiny = 15,
// eExtraSmall = 20,
// eSmall = 30,
// eMedium = 48,
// eBig = 50,
// eGigantic = 70
};
enum class ButtonStyle
{
eAll = 1,
eTextOnly,
eIconOnly,
eTextNextToIcon
};
enum class LayoutArea
{
eUnknown,
eInfoTab,
eDialogTab,
eSpecialTab,
eMainTab,
};
enum class ExercisePart
{
ePlanning,
eExecution,
eAfterAction,
eSpecial,
};
// Damit Q_DECLARE_OPERATORS_FOR_FLAGS funktioniert muss das Enum in einem namespace gekapselt sein
namespace ExerciseState
{
enum State
{
// the shift amount has to be the value of the Corresponding enum (EXM::ExerciseState) entry minus one
eUnknown = 0,
eCreated = 1 << 0,
eStarted = 1 << 1,
ePaused = 1 << 2,
eStopped = 1 << 4,
eTerminated = 1 << 5,
eAll = eCreated | eStarted | ePaused | eStopped | eTerminated
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<State>)
}
// convenience
typedef ExerciseState::State ExcState;
typedef QFlags<ExcState> ExerciseStates;
enum class CreateInstantType
{
DontCreateInstant,
CreateInstantAndAddAction,
CreateInstantAndDiscardAction,
};
struct PluginData
{
// QWidget *createFunc(QWidget *parent);
using createWidgetFuncPtr = std::function<LtsDialog*(QWidget *)>;
using directFuncPtr = std::function<void(const LtsWidgets::PluginData &)>;
createWidgetFuncPtr createWidgetFunc = nullptr;
directFuncPtr directFunc = nullptr;
QStringList iconPaths;
QIcon menuIcon;
QString tooltip;
qulonglong dropHandlerKey = 0;
QString menuText;
QString menuGroup;
LtsUtils::LtsTranslatableIdentifier translatableMenuGroup;
QString identifier; // uuid
bool closable = true;
LayoutArea layoutArea = LayoutArea::eDialogTab;
int operatorRight = 0;
CreateInstantType createInstant = CreateInstantType::DontCreateInstant;
bool visibleInMenu = true; // add to menu tab
ExercisePart exercisePart = ExercisePart::eSpecial;
ExerciseStates exerciseStates = ExerciseState::eAll;
PluginData() = default;
PluginData(const QString &_menuGroup,
const QString &_menuText,
const QString &_identifier,
const createWidgetFuncPtr &_cwfp,
int _operatorRight,
ExercisePart _exercisePart = ExercisePart::eSpecial,
ExerciseStates _exerciseStates = ExerciseState::eAll)
: PluginData(_menuGroup, _menuText, _identifier, QIcon(), _cwfp, _operatorRight, _exercisePart, _exerciseStates)
{}
PluginData(const QString &_menuGroup,
const QString &_menuText,
const QString &_identifier,
const QIcon &_icon,
const createWidgetFuncPtr &_cwfp,
int _operatorRight,
ExercisePart _exercisePart = ExercisePart::eSpecial,
ExerciseStates _exerciseStates = ExerciseState::eAll)
: createWidgetFunc(_cwfp)
, menuIcon(_icon)
, menuText(_menuText)
, menuGroup(_menuGroup)
, identifier(_identifier)
, operatorRight(_operatorRight)
, exercisePart(_exercisePart)
, exerciseStates(_exerciseStates)
{
}
static PluginData createGroup(const QString &menuGroup,
const QString &menuText,
const QString &identifier,
int operatorRight)
{
return PluginData(menuGroup, menuText, identifier, nullptr, operatorRight, ExercisePart::eSpecial);
}
bool isValid() const { return createWidgetFunc != nullptr; }
};
/**
* Dummy class to be able to display enums defined outside of a qobject
* as properties in designer
*/
class LTSWIDGETS_EXPORT Designer
{
Q_GADGET
public:
enum ButtonSize
{
eDefault = static_cast<int>(LtsWidgets::ButtonSize::eDefault),
// eTiny = static_cast<int>(LtsWidgets::ButtonSize::eTiny),
// eExtraSmall = static_cast<int>(LtsWidgets::ButtonSize::eExtraSmall),
// eSmall = static_cast<int>(LtsWidgets::ButtonSize::eSmall),
// eMedium = static_cast<int>(LtsWidgets::ButtonSize::eMedium),
// eBig = static_cast<int>(LtsWidgets::ButtonSize::eBig),
// eGigantic = static_cast<int>(LtsWidgets::ButtonSize::eGigantic)
};
Q_ENUM(ButtonSize)
enum ButtonStyle
{
eAll = static_cast<int>(LtsWidgets::ButtonStyle::eAll),
eTextOnly = static_cast<int>(LtsWidgets::ButtonStyle::eTextOnly),
eIconOnly = static_cast<int>(LtsWidgets::ButtonStyle::eIconOnly),
eTextNextToIcon = static_cast<int>(LtsWidgets::ButtonStyle::eTextNextToIcon)
};
Q_ENUM(ButtonStyle)
enum LayoutArea
{
eUnknownArea = static_cast<int>(LtsWidgets::LayoutArea::eUnknown),
eInfoTab = static_cast<int>(LtsWidgets::LayoutArea::eInfoTab),
eDialogTab = static_cast<int>(LtsWidgets::LayoutArea::eDialogTab),
eSpecialTab = static_cast<int>(LtsWidgets::LayoutArea::eSpecialTab),
eMainTab = static_cast<int>(LtsWidgets::LayoutArea::eMainTab)
};
Q_ENUM(LayoutArea)
};
}
Q_DECLARE_METATYPE(LtsWidgets::Designer::ButtonSize)
Q_DECLARE_METATYPE(LtsWidgets::Designer::ButtonStyle)
Q_DECLARE_METATYPE(LtsWidgets::Designer::LayoutArea)
Q_DECLARE_METATYPE(LtsWidgets::ButtonSize)
Q_DECLARE_METATYPE(LtsWidgets::ButtonStyle)
Q_DECLARE_METATYPE(LtsWidgets::LayoutArea)
+645
View File
@@ -0,0 +1,645 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsBaseLayoutWidget</class>
<widget class="QWidget" name="LtsBaseLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>768</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1024</width>
<height>768</height>
</size>
</property>
<property name="windowTitle">
<string>Operator Application</string>
</property>
<property name="windowIcon">
<iconset resource="resources/lts_resources.qrc">
<normaloff>:/rde_icon_256.png</normaloff>:/rde_icon_256.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="topLayoutWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lbRdeSplitterLeft">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">RDE_SPLITTER_LEFT</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbProjectLogo">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;PROJECT_LOGO&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lbAppName">
<property name="toolTip">
<string>App name</string>
</property>
<property name="text">
<string notr="true">%APP_NAME%</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bulle0">
<property name="text">
<string notr="true">•</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbCurrentTime">
<property name="toolTip">
<string>System time</string>
</property>
<property name="text">
<string notr="true">%CUR_TIME%</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="lbExerciseName">
<property name="toolTip">
<string>Exercise name</string>
</property>
<property name="text">
<string notr="true">%EX_NAME%</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bullet1">
<property name="text">
<string notr="true">•</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbExercisePhase">
<property name="toolTip">
<string>Exercise phase</string>
</property>
<property name="text">
<string notr="true">%EX_PHASE%</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bullet2">
<property name="text">
<string notr="true">•</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbExerciseStatus">
<property name="toolTip">
<string>Exercise status</string>
</property>
<property name="text">
<string notr="true">%EX_STATUS%</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bullet3">
<property name="text">
<string notr="true">•</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbExerciseTime">
<property name="toolTip">
<string>Exercise time</string>
</property>
<property name="text">
<string notr="true">%EX_TIME%</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bullet4">
<property name="text">
<string notr="true">•</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbPartitionType">
<property name="toolTip">
<string>Session type (Live or Replay)</string>
</property>
<property name="text">
<string notr="true">%PART_TYPE%</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbCustomTextBullet">
<property name="text">
<string notr="true">•</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbCustomText">
<property name="toolTip">
<string>Session type (Live or Replay)</string>
</property>
<property name="text">
<string notr="true">%CUSTOM_TEXT%</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="LtsWidgets::LtsToolBarArea" name="toolBar">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="notificationLayoutWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3"/>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbDebug">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string notr="true">[debug]</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsProfileButton" name="profileButton">
<property name="minimumSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsSplitter" name="masterSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="handleWidth">
<number>10</number>
</property>
<widget class="LtsWidgets::LtsTabWidget" name="infoWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="movable">
<bool>true</bool>
</property>
</widget>
<widget class="LtsWidgets::LtsSplitter" name="mainSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="handleWidth">
<number>10</number>
</property>
<widget class="LtsWidgets::LtsTabWidget" name="mainWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="toolTip">
<string notr="true"/>
</property>
<property name="movable">
<bool>true</bool>
</property>
</widget>
<widget class="LtsWidgets::LtsTabWidget" name="detailWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="movable">
<bool>true</bool>
</property>
</widget>
</widget>
<widget class="LtsWidgets::LtsSplitter" name="rightSplitter">
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="handleWidth">
<number>10</number>
</property>
<widget class="QWidget" name="naviWidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="pbPlanning">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Planning</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="pbExecution">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Execution</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pbAfterAction">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>After Action</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="pbSpecial">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Extras</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="QStackedWidget" name="stackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>100</height>
</size>
</property>
<widget class="LtsWidgets::LtsToolBox" name="naviActionsPlanning">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
</widget>
<widget class="LtsWidgets::LtsToolBox" name="naviActionsExecution">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
</widget>
<widget class="LtsWidgets::LtsToolBox" name="naviActionsAfterAction">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
</widget>
<widget class="LtsWidgets::LtsToolBox" name="naviActionsSpecial">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="LtsWidgets::LtsTabWidget" name="specialWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="movable">
<bool>true</bool>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsProfileButton</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_profile_button.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsSplitter</class>
<extends>QSplitter</extends>
<header>LtsWidgets/lts_splitter.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsTabWidget</class>
<extends>QTabWidget</extends>
<header>LtsWidgets/lts_tab_widget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsToolBarArea</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_tool_bar_area.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsToolBox</class>
<extends>QToolBox</extends>
<header>LtsWidgets/lts_tool_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="resources/lts_resources.qrc"/>
</resources>
<connections/>
</ui>
File diff suppressed because it is too large. Load diff
+450
View File
@@ -0,0 +1,450 @@
/**
* @RDE_HEADER@
* Base Layout Widget with all Areas (Info, Main, Detail, Menu)
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_base.h"
#include "lts_widgets_export.h"
#include "lts_notification_box.h"
#include "qevent.h"
#include <memory>
#include <QMap>
#include <QPointer>
#include <QWidget>
class QButtonGroup;
class QPropertyAnimation;
class QPushButton;
class QTimer;
class QToolButton;
namespace Ui
{
class LtsBaseLayoutWidget;
}
namespace IDL
{
enum class PartitionType;
}
namespace LtsUtils { class Promise; }
namespace LtsWidgets
{
class LtsDragIcon;
class LtsProgressDialogHelper;
class LtsTabWidget;
class LtsToolBarArea;
class LtsToolBox;
struct ExerciseData
{
QString exName;
QString exDescription;
int phId = -1;
QString phName;
QString phDescription;
QString exStateString; // Already translated. Todo: Probably won't change, if Language is changed on the fly.
ExcState exState = ExerciseState::eUnknown;
QColor exStateCol;
QString partitionType;
bool operator==(const ExerciseData &o) const
{
return exName == o.exName &&
exDescription == o.exDescription &&
phId == o.phId &&
phName == o.phName &&
phDescription == o.phDescription &&
exStateString == o.exStateString &&
exState == o.exState &&
exStateCol == o.exStateCol &&
partitionType == o.partitionType;
}
bool operator!=(const ExerciseData &o) const
{
return !operator ==(o);
}
};
/**
* @brief The LtsBaseLayoutWidget class represents the Patric-Layout-System (all Areas: Info, Main, Detail, Menu), which is used as stand-alone widget per Screen.
* It is created per Session by the LtsSession, created by the LtsSessionManager whenever a new Session was created.
*/
class LTSWIDGETS_EXPORT LtsBaseLayoutWidget : public QWidget
{
Q_OBJECT
public:
explicit LtsBaseLayoutWidget(QWidget *parent = nullptr, bool splitMenu = true);
~LtsBaseLayoutWidget();
/**
* @brief hideVisibleLayouts hides all layouts without interfering layout sizes and saves all layout's old states that can be restored by calling resetLayoutVisibility
*/
void hideVisibleLayouts();
/**
* @brief resetLayoutVisibility reverts the actions done by hideVisibleLayouts - reshows all hidden layouts
*/
void resetLayoutVisibility();
/**
* @brief setMenuVisible sets the visibility of the menu (and the special tab, too!)
* @param visible true to reshow the menu and special tab, false to completely hide them
*/
void setMenuVisible(bool visible);
/**
* @brief isMenuVisible returns the visibility state of the menu (and special tab, too!)
* @return true if both are visibly, false if not
*/
bool isMenuVisible() const;
bool isTopBarVisible() const;
void setTopBarVisible(bool visible);
/**
* @brief isMenuCollapsed returns if the menu was collapsed programmatically by calling clappseMenu.
* NOTE: does not consider the collapsed state if the user collapsed the menu himself by reducing the menu size to zero by moving the splitter.
* @return true is collapsed before, false if menu is not size zero.
*/
bool isMenuCollapsed() const;
/**
* @brief collapseMenu reduces the size of the menu to zero, but the visibility doesn't change, so that the user can still restore the menu with the splitter.
* NOTE: the special tab widget is collapsed, too.
* @param collapse true to collapse, false to restore to the state before the collapse.
*/
void collapseMenu(bool collapse = true);
/**
* @brief setSessionInfo sets the informations about the Session of this BaseLayout, so that the window can request a Session close (by logging out) when this window receives a close request.
*/
void setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId);
/**
* @brief addMenuAction adds an action (aka. menu entry) to the menu layout. The Action will be added into the given group.
* @param plData Action data with information about where to put the menu entry and what to do when the menu entry is clicked
* @return the LtsDragIcon that was now created and put into the menu layout
*/
LtsDragIcon *addMenuAction(const PluginData &plData, bool isFirstRun = false);
/**
* @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the information in the given plData
* @param plData Action data to identify the menu entry with (group name + captioin text of the menu entry)
* @return true when found & deleted, false when not found and also not deleted
*/
bool removeMenuAction(const PluginData &plData);
/**
* @brief removeMenuAction removes an action (aka. menu entry) previously created by the addMenuAction method by identifying it by the pointer value
* @param icon to identify the action (aka. menu entry) by the menu entry the LtsDragIcon that was created by the addMenuAction method and put into the menu layout
* @return true when found & deleted, false when not found and also not deleted (as it was deleted before)
*/
bool removeMenuAction(LtsDragIcon *icon);
bool removeMenuAction(const QString &byIdentifier);
/**
* @brief clearMenuActions deletes all actions (aka. menu entries) in the menu layout
*/
void clearMenuActions();
void setAppName(const QString &appName);
bool isDialogFocused(const LtsDialog *dialog) const;
/**
* @brief setCustomText shows a custom text to the top layout of the base layout, right after the partition type text.
* Also prepends a bullet before the text to clearly differenciate between the partition type text and the custom text.
* Call with an empty string to hide the custom text again.
* @param text to print on the label
*/
void setCustomText(const QString &text);
const QString customText() const;
/**
* @brief getTabFromLayoutArea returns the pointer to the LtsTabWidget that is of the type LayoutArea 'area'
* @param area the area of which the LtsTabWidget's type of
* @return the pointer to the LtsTabWidget
*/
LtsTabWidget *getTabFromLayoutArea(LayoutArea area) const;
/** TODO: rename to getDialog
* @brief getWidget returns the Dialog which is located in the given LayoutArea, identified by the group + identifier strings
* @param menuGroup the name of the main tab of the LayoutArea
* @param identifier the name of the sub tab (when sub-tabbed) of the LayoutArea, otherwise the identifier is added to the menuGroup string
* @param area the LayoutArea (/LtsTabWidget) the Dialog is located
* @return the LtsDialog when found, otherwise nullptr when not found
*/
LtsDialog *getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown);
/**
* @brief getMenu returns the Widget of the Menu Layout Area (as the Menu is not an actual LayoutArea (not an LtsTabWidget which could be returned by 'getTabFromLayoutArea')
* @return the pointer to the Widget of the Menu Layout
*/
QWidget *getMenu() const;
/**
* @brief getToolbar returns the LtsToolBarArea of the Menu Layout Area
* @return the pointer to the LtsToolBarArea of the Menu Layout
*/
LtsToolBarArea *getToolbar() const;
/**
* @brief setOperatorName sets the operator name of this session's operator and sets the operator name widget's text
* @param operatorName the human readable operator name of this session's operator
*/
void setOperatorName(const QString &operatorName, bool showTerminalButton);
/**
* @brief setProfilePicture sets the profile picture of this session's operator and sets the profile picture widget's picture
* @param profilePicture the profgile picture of this session's operator
*/
void setProfilePicture(const QPixmap &profilePicture);
/**
* @brief setExerciseData sets the name and phase of this Exercise (if it is an Exercise) of this session - only sets the text of a label (label is hidden of not set)
* @param exerciseData necessary data to show
*/
void setExerciseData(const ExerciseData &data);
/**
* @brief Call this function when the base layout was properly constructed so baselayout can do some internal stuff
*/
void initilizationFinished();
/**
* @brief addNotification [Do not use.] used by LtsSession to add a Notification to the Notification Bar, but as a QToolButton
* @param noti the noti to add to the Notification Bar
*/
void addNotification(QToolButton *noti);
/**
* @brief isWidgetsHidden returns the visibility state of the detail tab - the state changes with calling either of
* these methods: onReShowAnimated, onHideAnimated, showWidgets, hideWidgets
* @return detail tab visible (not to be confused with resizing the detail widget with the splitter, that only
* minized the tab, but the tab is still visible)
*/
bool isWidgetsHidden() const;
bool notificationExists(const QString &identifier) const;
const QMap<LtsDragIcon *, PluginData> &mapButtons() const { return m_mapButtons; }
void setToolBarActions(const QStringList &actions);
QStringList toolBarActions() const;
void setFunctionKeys(const QMap<int, QString> &actions);
QMap<int, QString> functionKeys() const;
void setWorkspaceId(const QString &workspaceId) { m_workspaceId = workspaceId; }
/**
* @brief showMessage adds a notification bubble to the bottom right of the blw. Text can be formatted with HTML.
* If a message is already visible, it will be replaced and will be shown after the new message was closed.
* A message with the same identifier will be replaced in the queue or if currently shown.
* @param title The title in bold which is show obove the text.
* @param text The text to show in the bubble - should be as short as possible as bubbles are small. Can be HTML.
* @param type The type which sets the color and icon the bubble should have. See LtsNotificationBox::MessageType.
* @param clickedLinkCallback The function to call if a link inside the bubble has been clicked (HTML 'a' tag)
* @param closeOnLinkClicked Closes the bubble if a link has been clicked. Note: queued messages (if any) will be shown next
* @param identifier An invisible identifier a message will be queued with. Can be left empty. If another message with the
* same identifier (if not empty) is shown, the other message with the same identifier will be removed from the queue or if
* it's the shown message, it will be closed.
*/
void showMessage(const QString &title, const QString &text, LtsNotificationBox::MessageType type = LtsNotificationBox::MessageType::Info, const std::function<void(const QString &link)> &clickedLinkCallback = nullptr, bool closeOnLinkClicked = true, const QString &identifier = {});
bool messageIdentifierExists(const QString &identifier) const;
void executeAction(const QString &actionIdentifier);
bool isSecondaryWidget() const { return m_isSecondaryWidget; } // for the LtsScreenContainer to determine if this is a 'Hilfsansicht' for GUEZ SWP 2023 (https://devhub.defence-elec.de/confluence/x/yLjeDg) (siehe 1.1.1)
void setSecondaryWidget() { m_isSecondaryWidget = true; }
/**
* @brief Remove Messages from Queue, as it's handled by someones else
* @param message identifier to remote from que or diplay.
*/
void removeQueuedMessage(const QString &messageIdentifier);
public slots:
bool slotClose(); // checks for unsaved changes first and aborts if there are any
void slotLogout(); // checks for unsaved changes first and aborts if there are any
void slotScreenCountChanged(int count);
/**
* @brief onMenuEntryClicked is triggered when an action (aka. menu entry) in the menu layout is clicked, executes the action's payload (or focuses the coherant dialog when already open)
*/
void onMenuEntryClicked();
void onHideAnimated();
void onReShowAnimated();
/**
* @brief addWidget adds a widget to the operator-specified base layout (either selection by cursor or by permanent selectioin) NOTE: deletes dialog if it already exists
* @param widget the Dialog to be added
* @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into
* @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs
* @param area the LayoutArea to add the Dialog into
* @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator
* @return true when added, false when not (invalid widget or non-existant LayoutArea)
*/
bool addWidget(LtsWidgets::LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true);
/**
* @brief Like addWidget addOrReplaceWidget adds a dialog widget to base layout with the difference that an existing widget with same identifier is removed from layout before adding he new one.
* The new dialog will not be deleted (compare with adddWidget)
* @param widget the Dialog to be added
* @param menuGroup the main tab identified by 'menuGroup' (tab is created when tab not existent) to add the Dialog into
* @param identifier the sub tab (when LtsTabWidget is sub-tabbed, otherwise only added to tab identified by menuGroup + identifier), identified by 'identifier' to add the Dialog into - keep empty to prevent checking for existing tabs
* @param area the LayoutArea to add the Dialog into
* @param setClosable true when closable by operator (shows a close-button in the tab), false when not closable by operator
* @return true when added, false when not (invalid widget or non-existant LayoutArea)
* @see addWidget
*/
bool addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area = LayoutArea::eUnknown, bool setClosable = true, bool focusNewTab = true );
bool focusTab(const QString &menuGroup, const QString &identifier, LtsWidgets::LayoutArea area);
/**
* @brief executeCreateInstantFunctions executes both the createWidgetFunc and the directFunc (if available). (only used by LtsSession & this addMenuAction)
* @param plData
*/
void executeCreateInstantFunctions(const LtsWidgets::PluginData &plData);
void setExerciseTime(qint64 mSecsSinceEpoch);
bool haveUnsavedChanges();
/**
* @brief notifyUnsavedChanges
* @return true if there were NO unsaved changes (method 'failed') and vice versa
*
*/
bool notifyUnsavedChanges();
// LtsUtils::Promise *notifyUnsavedChangesAsync();
void setIrreplaceableOnce() { m_irreplaceableOnce = true; }
bool irreplaceableReset() { bool r = m_irreplaceableOnce; m_irreplaceableOnce = false; return r; }
bool isIrreplaceable() const { return m_irreplaceableOnce; }
/**
* @brief showWidgets shows the Dialog Layout again after it was previously hidden
*/
void showWidgets();
/**
* @brief hideWidgets hides the visible Dialog Layout (for example e.g. when more space is needed for selection on the map)
*/
void hideWidgets();
private slots:
/**
* @brief slotOperatorMenuClicked shows a menu to change things affecting the operator (logout, etc.)
*/
void slotOperatorMenuClicked();
void setCurrentTime();
signals:
/**
* @brief signalCloseRequest is emitted by the closeEvent and requests a logout / close of the current Session (if this is the last BaseLayout)
*/
//void signalCloseRequest(const int domainId, const IDL::PartitionType &partitionType, const int partitionId);
void signalClose();
void signalLogout();
void signalShowOperatorSettings();
void signalRetranslateActions();
void signalSwap();
void signalStapleTopicLockHandler(LtsWidgets::LtsDialog *widget);
void signalSuccessfullyPlaced(); // called by MMM if this widget was successfully placed (even works if MMM is inactive (off))
/**
* Notify about unsaved changes before popup message box.
*/
void signalNotifyUnsavedChanges(bool &reject);
protected:
/**
* @brief closeEvent requests to close a session. As a BaseLayout is linked to a Session, the Session must be closed before allowing
* the BaseLayout to close. So a logout is executed before closing the BaseLayout.
* TODO: One or more BaseLayouts can be linked to a Session - look if it's the last BaseLayout
* @param event
*/
void closeEvent(QCloseEvent *event) override;
void showEvent(QShowEvent *event) override;
void changeEvent(QEvent *e) override;
private:
void addDebugMenu();
void retranslate();
void addTouchScroll(QWidget *widget);
LtsDragIcon *createButton(const PluginData &plData);
int getIndexOfTab(LtsTabWidget *tab);
LtsToolBox *getToolBox(const ExercisePart &exercisePart) const;
QPushButton *getExercisePartButton(const ExercisePart &exercisePart) const;
void checkExercisePartButtonVisibility();
void checkMenuGroup(const QString &menuGroup, const ExercisePart &exercisePart);
bool isDialogFocused(const LtsDialog *dialog, const LayoutArea &layoutArea) const;
bool isDialogFocused(const LtsDialog *dialog, const LtsTabWidget *tab) const;
void applyExerciseStateRestricition();
void executePluginData(const PluginData &plData);
void showNextQueuedMessage();
//void removeQueuedMessage(const QString &messageIdentifier);
struct Message
{
public:
QString title;
QString text;
LtsNotificationBox::MessageType type;
std::function<void(const QString &link)> clickedLinkCallback;
bool closeOnLinkClicked;
QString identifier;
};
ExerciseData m_exerciseData;
QMap<LtsDragIcon *, PluginData> m_mapButtons;
QList<int> m_mainSplitterSizes;
QList<int> m_menuSplitterSizes;
bool m_isHidden;
QPropertyAnimation *m_currentAnimation{nullptr};
Ui::LtsBaseLayoutWidget *m_ui{nullptr};
IDL::PartitionType m_partitionType;
int m_domainId;
int m_partitionId;
QString m_operatorName;
bool m_firstShow;
bool m_showTerminalButton;
qint64 m_exerciseTime;
QTimer *m_setCurrentTimeTimer{nullptr};
bool m_irreplaceableOnce;
QButtonGroup *m_bg{nullptr};
QString m_workspaceId;
int m_screenCount;
bool m_menuCollapsed;
QMap<QPointer<LtsWidgets::LtsTabWidget>, bool> m_oldLayoutVisibilityStates;
LtsNotificationBox *m_notiBox{nullptr};
QStringList m_notFoundYet;
bool m_isSecondaryWidget { false };
QVector<Message> m_currentMessage; // optional
QVector<Message> m_messageQueue;
};
} // namespace LtsWidgets
+135
View File
@@ -0,0 +1,135 @@
/**
* @RDE_HEADER@
* Bitfield Widget for bitwise displaying and editing of quint8 values
* Created by Christian Kaemmerer
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_bitfield_widget.h"
#include <QPainter>
namespace LtsWidgets
{
LtsBitfieldWidget::LtsBitfieldWidget(QWidget *parent, quint8 value)
: QWidget(parent)
, m_value(value)
{
setObjectName(QString::fromUtf8("LtsBitfieldWidget"));
}
void LtsBitfieldWidget::paintEvent(QPaintEvent *pe)
{
QWidget::paintEvent(pe);
QPainter painter;
painter.begin(this);
painter.setPen(Qt::SolidLine);
if (m_hasCaption)
{
QFont font = painter.font();
font.setPointSizeF( (height()/2.0 > width()/8.0) ? width()/8.0 : height()/2.0 );
painter.setFont(font);
}
for (int i = 0; i < 8; ++i)
{
QRectF bitGraphic;
bitGraphic.setLeft(width() / 8.0 * i);
bitGraphic.setWidth((width() / 8.0) - 1);
bitGraphic.setHeight(height() - 1);
bool bitVal = (m_value & (1 << (7 - i))) > 0;
painter.fillRect(bitGraphic, QBrush(bitVal ? m_onColor : m_offColor, Qt::SolidPattern));
if (m_hasCaption)
{
painter.setPen(bitVal ? m_offColor : m_onColor);
painter.drawText(bitGraphic, Qt::AlignCenter, bitVal ? QStringLiteral("1") : QStringLiteral("0"));
}
painter.setPen(Qt::gray);
painter.drawRect(bitGraphic);
} // for(..)
painter.end();
}
void LtsBitfieldWidget::setHasCaption(bool hasCaption)
{
if (m_hasCaption == hasCaption)
return;
m_hasCaption = hasCaption;
repaint();
emit hasCaptionChanged(hasCaption);
}
void LtsBitfieldWidget::setOnColor(const QColor &onColor)
{
if (m_onColor == onColor)
return;
m_onColor = onColor;
repaint();
}
void LtsBitfieldWidget::setOffColor(const QColor &offColor)
{
if (m_offColor == offColor)
return;
m_offColor = offColor;
repaint();
}
void LtsBitfieldWidget::mousePressEvent(QMouseEvent *event)
{
if (!m_isEditable)
{
QWidget::mousePressEvent(event);
return;
}
for (int i = 0; i < 8; ++i)
{
if (event->pos().x() > width()/8 * i &&
event->pos().x() < width()/8 * (i + 1))
{
m_value ^= (1 << (7 - i));
emit valueChanged(m_value);
break;
}
}
repaint();
}
void LtsBitfieldWidget::setEditable(bool editable)
{
if (m_isEditable == editable)
return;
m_isEditable = editable;
emit isEditableChanged(m_isEditable);
}
void LtsBitfieldWidget::setValue(quint8 value)
{
if (m_value == value)
return;
m_value = value;
repaint();
emit valueChanged(m_value);
}
} // namespace
+141
View File
@@ -0,0 +1,141 @@
/**
* @RDE_HEADER@
* Bitfield Widget for bitwise displaying and editing of quint8 values
* Created by Christian Kaemmerer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QWidget>
#include <QMouseEvent>
#include "lts_widgets_export.h"
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsBitfieldWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(quint8 value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(bool isEditable READ isEditable WRITE setEditable NOTIFY isEditableChanged)
Q_PROPERTY(bool hasCaption READ hasCaption WRITE setHasCaption NOTIFY hasCaptionChanged)
Q_PROPERTY(QColor onColor READ onColor WRITE setOnColor)
Q_PROPERTY(QColor offColor READ offColor WRITE setOffColor)
public:
explicit LtsBitfieldWidget(QWidget *parent = nullptr, quint8 value = 0);
signals:
void valueChanged(quint8 newValue);
void isEditableChanged(bool option);
void hasCaptionChanged(bool option);
public slots:
/**
* @brief setvalue
* sets m_value to the given quint8
* @param quint8 value
*/
void setValue(quint8 value);
/**
* @brief setIsEditable
* sets m_isEditable to option.
* true meaning the value of the widget can be changed via mousePressEvent
* @param bool option
*/
void setEditable(bool editable);
/**
* @brief setHasCaption
* sets m_hasCaption to 0 or 1 which toggels if text should be
* drawn on each bitGraphic
* @param bool hasCaption
*/
void setHasCaption(bool hasCaption);
/**
* @brief setOnColor
* sets the onColor for m_bitGraphic
* @param QColor onColor
*/
void setOnColor(const QColor &onColor);
/**
* @brief setOffColor
* sets the offColor for m_bitGraphic
* @param QColor offColor
*/
void setOffColor(const QColor &offColor);
public:
/**
* @brief value
* get the current value
* @return quint8 of current value
*/
quint8 value() const { return m_value; }
/**
* @brief isEditable
* get the current state of editability
* @return bool of editability state
*/
bool isEditable() const { return m_isEditable; }
/**
* @brief hasCaption
* gets state of m_hasCaption
* @return bool hasCaption
*/
bool hasCaption() const { return m_hasCaption; }
/**
* @brief onColor
* gets the onColor for m_bitGraphic
* @return QColor m_onColor
*/
QColor onColor() const {return m_onColor;}
/**
* @brief offColor
* gets the offColor for m_bitGraphic
* @return QColor m_offColor
*/
QColor offColor() const {return m_offColor;}
private:
/**
* @brief mousePressEvent
* Checks if the click position falls within any of the bit rectangles. If there
* is a match the value of that bit gets flipped
* @param event QMouseEvent
*/
void mousePressEvent(QMouseEvent *event) override;
/**
* @brief paintEvent
* Draws m_bitGraphic.Size() rectangles and depending on the bit value of m_value draw either
* a onColor rectangle with a offColor 1 or a offColor rectangle with a onColor 0
* @param pe QPaintEvent (isn't used to do anything)
*/
void paintEvent(QPaintEvent *pe) override;
private:
quint8 m_value = 0;
bool m_isEditable = true;
bool m_hasCaption = true;
QColor m_onColor = Qt::black;
QColor m_offColor = Qt::white;
};
} // namespace LtsWidgets
+137
View File
@@ -0,0 +1,137 @@
/**
* @RDE_HEADER@
*
* $Id$
* $HeadURL$
* $Author$
* Created by Malte Diers, SLSE, 2017-10-10
*/
#pragma once
#include "lts_abstract_table_model.h"
namespace LtsWidgets
{
template<class T>
class LtsCheckableTableModel : public LtsAbstractTableModel<T>
{
public:
LtsCheckableTableModel(const QVector<QString> &header, int checkableColumn, QObject *parent)
: LtsAbstractTableModel<T>(header, parent)
, m_checkableColumn(checkableColumn)
{
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (role == Qt::CheckStateRole && index.column() == m_checkableColumn && LtsAbstractTableModel<T>::indexIsValid(index))
return LtsAbstractTableModel<T>::m_vecData.at(index.row()).first ? Qt::Checked : Qt::Unchecked;
return QVariant();
}
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
{
if (role == Qt::CheckStateRole)
{
switchCheckbox(index);
return true;
}
return QAbstractItemModel::setData(index, value, role);
}
Qt::ItemFlags flags(const QModelIndex &index) const override
{
Qt::ItemFlags flags(LtsWidgets::LtsAbstractTableModel<T>::flags(index) & ~Qt::ItemIsSelectable);
if (index.column() == m_checkableColumn)
return flags.setFlag(Qt::ItemIsUserCheckable);
return flags;
}
QVector<int> getCheckedIds() const
{
QVector<int> ids;
for (int row = 0; row < LtsAbstractTableModel<T>::rowCount(); ++row)
{
const T &entry = LtsAbstractTableModel<T>::m_vecData.at(row);
if (entry.first)
ids << entry.second.id();
}
return ids;
}
QVector<int> getUncheckedIds() const
{
QVector<int> ids;
for (int row = 0; row < LtsAbstractTableModel<T>::rowCount(); ++row)
{
const T &entry = LtsAbstractTableModel<T>::m_vecData.at(row);
if (!entry.first)
ids << entry.second.id();
}
return ids;
}
bool setChecked(const QVector<int> &vecCheckedIds)
{
for (int row = 0; row < LtsAbstractTableModel<T>::rowCount(); ++row)
{
T &entry = LtsAbstractTableModel<T>::m_vecData[row];
bool bContains = vecCheckedIds.contains(entry.second.id());
if (entry.first != bContains)
{
entry.first = bContains;
const QModelIndex idx(LtsAbstractTableModel<T>::index(row, m_checkableColumn, QModelIndex()));
emit LtsAbstractTableModel<T>::dataChanged(idx, idx, {Qt::CheckStateRole});
}
}
return true;
}
bool isFiltered(const T &data, DataModification modType) const override
{
Q_UNUSED(data);
Q_UNUSED(modType);
return false;
}
public slots:
void switchCheckbox(const QModelIndex &index)
{
auto entry = LtsAbstractTableModel<T>::getRowData(index.row());
entry.first = !entry.first;
LtsAbstractTableModel<T>::setRowData(index.row(), entry);
}
void selectAll()
{
for (int row = 0; row < LtsAbstractTableModel<T>::rowCount(); ++row)
{
T entry = LtsAbstractTableModel<T>::m_vecData.at(row);
entry.first = true;
LtsAbstractTableModel<T>::setRowData(row, entry);
}
}
void unselectAll()
{
for (int row = 0; row < LtsAbstractTableModel<T>::rowCount(); ++row)
{
T entry = LtsAbstractTableModel<T>::m_vecData.at(row);
entry.first = false;
LtsAbstractTableModel<T>::setRowData(row, entry);
}
}
private:
int m_checkableColumn;
};
}
+162
View File
@@ -0,0 +1,162 @@
/**
* @RDE_HEADER@
* Advanced Filter, enables filtering per column
* $Id: lts_filter_proxy.cpp 27949 2016-06-22 18:31:13Z ehrlicher $
* $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/LtsSearchModel/lts_filter_proxy.cpp $
* $Author: ehrlicher $
* Created by Mike Wille, SLSE, 2016-03-14
*/
#include "lts_column_filter_proxy_model.h"
#include <LtsUtilsCore/variant_operators.h>
#include <QRegularExpression>
namespace LtsWidgets
{
LtsColumnFilterProxyModel::LtsColumnFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
setSortCaseSensitivity(Qt::CaseInsensitive);
}
bool LtsColumnFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
return LtsUtils::lessThan(sourceModel()->data(left), sourceModel()->data(right));
}
void LtsColumnFilterProxyModel::onSetColumnStringFilter(const QString &newFilterString, int column)
{
setColumnStringFilter(newFilterString, column);
}
void LtsColumnFilterProxyModel::onSetColumnDataFilter(const QVariant &newFilterData, int column)
{
setColumnDataFilter(newFilterData, column);
}
void LtsColumnFilterProxyModel::setColumnStringFilter(const QString &newFilterString, int column)
{
if (column < 0)
return;
auto it = m_columnStringFilters.find(column);
if (newFilterString.isEmpty())
{
if (it == m_columnStringFilters.end())
return;
m_columnStringFilters.erase(it);
}
else
{
auto rx = QRegularExpression(newFilterString,
QRegularExpression::CaseInsensitiveOption);
if (it == m_columnStringFilters.end())
{
m_columnStringFilters.insert(column, rx);
}
else
{
if (rx == it.value())
return;
it.value() = rx;
}
}
invalidateFilter();
}
void LtsColumnFilterProxyModel::setColumnDataFilter(const QVariant &newFilterData, int column)
{
if (column < 0)
return;
auto it = m_columnDataFilters.find(column);
if (newFilterData.isNull())
{
if (it == m_columnDataFilters.end())
return;
m_columnDataFilters.erase(it);
}
else
{
if (it == m_columnDataFilters.end())
{
m_columnDataFilters[column] = newFilterData;
}
else
{
if (newFilterData == it.value())
return;
it.value() = newFilterData;
}
}
invalidateFilter();
}
bool LtsColumnFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
// filtering
const auto sm = sourceModel();
bool bRet = true;
for (auto it = m_columnDataFilters.begin(); bRet && it != m_columnDataFilters.end(); ++it)
{
const QModelIndex idx = sm->index(source_row, it.key(), source_parent);
const QVariant _data = sm->data(idx, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole).toString(); // MW: variant => string => variant? is this intended?
bRet = _data == it.value();
if (!bRet)
break;
}
for (auto it = m_columnStringFilters.begin(); bRet && it != m_columnStringFilters.end(); ++it)
{
const QRegularExpression &rx = it.value();
if (!rx.isValid())
continue;
const QModelIndex idx = sm->index(source_row, it.key(), source_parent);
const QString _data = sm->data(idx, Qt::DisplayRole).toString();
bRet = _data.contains(rx);
if (!bRet)
break;
}
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
if (!bRet && m_bRecursiveFiltering)
{
const QModelIndex idx = sm->index(source_row, 0, source_parent);
if (checkIfOneChildIsVisible(idx))
return true;
}
#endif
return bRet;
}
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
void LtsColumnFilterProxyModel::setRecursiveFilteringEnabled(bool recursive)
{
if (m_bRecursiveFiltering == recursive)
return;
m_bRecursiveFiltering = recursive;
invalidateFilter();
}
bool LtsColumnFilterProxyModel::recursiveFilteringEnabled() const
{
return m_bRecursiveFiltering;
}
bool LtsColumnFilterProxyModel::checkIfOneChildIsVisible(const QModelIndex &parent) const
{
const auto sm = sourceModel();
const int childCount = sm->rowCount(parent);
for (int i = 0; i < childCount; ++i)
{
if (filterAcceptsRow(i, parent))
return true;
}
return false;
}
#endif
} // namespace LtsWidgets
+128
View File
@@ -0,0 +1,128 @@
/**
* @RDE_HEADER@
* Advanced Filter, enables filtering per column
* $Id: lts_filter_proxy.h 27949 2016-06-22 18:31:13Z ehrlicher $
* $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/LtsSearchModel/lts_filter_proxy.h $
* $Author: ehrlicher $
* Created by Mike Wille, SLSE, 2016-03-14
*/
#pragma once
#include <LtsWidgets/lts_widgets_export.h>
#include <LtsUtilsCore/variant_operators.h>
#include <QDataStream>
#include <QIcon>
#include <QRegularExpression>
#include <QSortFilterProxyModel>
#include <QVector>
namespace LtsWidgets
{
struct ComboboxFilterData
{
QString text; // visible text in ComboBox
QVariant data; // data cor comparison during filtering
QIcon icon; // decoration of text in ComboBox
inline bool operator<(const ComboboxFilterData &cfd)
{
return LtsUtils::lessThan(data, cfd.data);
}
inline friend QDataStream &operator<<(QDataStream &ds, const ComboboxFilterData &cfd)
{
ds << cfd.text << cfd.data << cfd.icon;
return ds;
}
inline friend QDataStream &operator>>(QDataStream &ds, ComboboxFilterData &cfd)
{
ds >> cfd.text >> cfd.data >> cfd.icon;
return ds;
}
};
struct ComboboxFilterContent
{
QVector<ComboboxFilterData> data;
bool sortItems = false; // sort items in ComboBox yes/no
bool isValid() const { return m_isValid; }
QVariant toVariant() const
{
if (!isValid())
return QVariant();
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << (*this);
return ba;
}
static ComboboxFilterContent fromVariant(const QVariant &v)
{
ComboboxFilterContent ret;
QDataStream ds(v.toByteArray());
ds.startTransaction();
ds >> ret;
ds.commitTransaction();
ret.m_isValid = ds.status() == QDataStream::Ok;
return ret;
}
inline friend QDataStream &operator<<(QDataStream &ds, const ComboboxFilterContent &cfc)
{
ds << cfc.data << cfc.sortItems;
return ds;
}
inline friend QDataStream &operator>>(QDataStream &ds, ComboboxFilterContent &cfc)
{
ds >> cfc.data >> cfc.sortItems;
return ds;
}
private:
bool m_isValid = true; // internal, will be set to true/false in fromVariant()
};
class LTSWIDGETS_EXPORT LtsColumnFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
static constexpr int ComboboxFilterRole = Qt::UserRole + 17;
explicit LtsColumnFilterProxyModel(QObject *parent = nullptr);
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
void setRecursiveFilteringEnabled(bool recursive);
bool recursiveFilteringEnabled() const;
#endif
const QHash<int, QRegularExpression> &columnStringFilters() const { return m_columnStringFilters; }
const QHash<int, QVariant> &columnDataFilters() const { return m_columnDataFilters; }
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
virtual void setColumnStringFilter(const QString &newFilterString, int column);
virtual void setColumnDataFilter(const QVariant &newFilterData, int column);
public slots:
void onSetColumnStringFilter(const QString &newFilterString, int column);
void onSetColumnDataFilter(const QVariant &newFilterData, int column);
protected:
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
bool checkIfOneChildIsVisible(const QModelIndex &parent) const;
#endif
private:
QHash<int, QRegularExpression> m_columnStringFilters;
QHash<int, QVariant> m_columnDataFilters;
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
bool m_bRecursiveFiltering = false;
#endif
};
} // namespace LtsWidgets
+75
View File
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsColumnOrganiserWidget</class>
<widget class="QWidget" name="LtsColumnOrganiserWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>419</width>
<height>402</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QTableView" name="tableView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>178</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="resetButton">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="applyButton">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+125
View File
@@ -0,0 +1,125 @@
/**
* @RDE_HEADER@
* ComboBox, optimized for tablets (bigger dropdown/popup and more comfortable completer)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#include "lts_combo_box.h"
#include "lts_selection_popup.h"
#include "lts_message_dialog.h"
#include <QGuiApplication>
#include <QShowEvent>
//#define NEVER
#ifdef NEVER
# include <QDebug>
#endif // NEVER
namespace LtsWidgets
{
bool LtsComboBox::s_useSelectionDialog = false;
LtsComboBox::LtsComboBox(QWidget *parent)
: QComboBox(parent)
, m_currentIndex(-1)
{
connect(this, &LtsComboBox::signalExternallyChanged,
this, &LtsComboBox::signalRefill); // sig on sig (to fetch cbx as sender)
}
void LtsComboBox::setUseSelectionDialog(bool bUseSelectionDialog)
{
s_useSelectionDialog = bUseSelectionDialog;
}
void LtsComboBox::showEvent(QShowEvent *e)
{
QComboBox::showEvent(e);
m_currentIndex = currentIndex();
}
void LtsComboBox::enterEvent(QEvent *e)
{
BaseClass::enterEvent(e);
emit signalEnterEvent();
}
void LtsComboBox::leaveEvent(QEvent *e)
{
BaseClass::leaveEvent(e);
emit signalLeaveEvent();
}
void LtsComboBox::showPopup()
{
bool bUseSelectionDialog = s_useSelectionDialog;
if (QGuiApplication::queryKeyboardModifiers() == Qt::ControlModifier)
bUseSelectionDialog = !bUseSelectionDialog;
if (!bUseSelectionDialog)
{
QComboBox::showPopup(); // show original popup when CTRL is pressed
return;
}
if (!model()->rowCount()) // nothing to show? tell the user.
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("There are no items available!"));
return;
}
// create the touch-friendly, screen-centered custom popup and show it
LtsSelectionPopup* popup = new LtsSelectionPopup(model(), currentIndex(), this);
popup->setAttribute(Qt::WA_DeleteOnClose); // dialog must delete itself, as there is no Dialog Helper
#ifdef NEVER
// debugging :
// // highlighted -> UserInteraction
// connect(popup, static_cast<void (LtsSelectionPopup::*)(int)>(&LtsSelectionPopup::highlighted), this, [](int idx){
// qCritical() << " LtsComboBox: called: highlighted(int), idx=" << idx;
// });
// currentIndexChanged -> programmatically & UserInteraction
connect(popup, static_cast<void (LtsSelectionPopup::*)(int)>(&LtsSelectionPopup::currentIndexChanged), this, [](int idx){
qCritical() << " LtsComboBox: called: currentIndexChanged(int), idx=" << idx;
});
// activated -> UserInteraction
connect(popup, static_cast<void (LtsSelectionPopup::*)(int)>(&LtsSelectionPopup::activated), this, [](int idx){
qCritical() << " LtsComboBox: called: activated(int), idx=" << idx;
});
// : debugging
#endif // NEVER
// Although we derive from QComboBox, there seems to be no signal emitted implicitly.
// Therefore we have to listen to our popup:
connect(popup, &LtsSelectionPopup::currentIndexChanged,
this, &LtsComboBox::userChangedIndex); // listen on changes
connect(popup, &LtsSelectionPopup::activated,
this, static_cast<void (LtsComboBox::*)(int)>(&LtsComboBox::activated)); // SIGNAL on SIGNAL !
popup->show();
}
void LtsComboBox::userChangedIndex(int index)
{
m_currentIndex = index;
setCurrentIndex(index);
}
bool LtsComboBox::hasValueChanged() const
{
if (m_currentIndex == -1)
return false;
return !(currentIndex() == m_currentIndex);
}
} // namespace LtsWidgets
+76
View File
@@ -0,0 +1,76 @@
/**
* @RDE_HEADER@
* ComboBox, optimized for tablets (bigger dropdown/popup and more comfortable completer)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#pragma once
#include "lts_widgets_export.h"
#include <QComboBox>
namespace LtsWidgets
{
/**
* @brief The LtsComboBox class provides an almost identically version of the QComboBox, with the main difference that this version is more touch friendly.
* The LtsComboBox's Popup (LtsComboBoxPopup) is a LtsFramelessDialog (screen-centered) with bigger selection-items.
* For debug purpose: press CTRL while clicking on the popup-button to open the original QComboBox-Popup
*/
class LTSWIDGETS_EXPORT LtsComboBox : public QComboBox // TODO: change title of selection window
{
Q_OBJECT
using BaseClass = QComboBox;
public:
explicit LtsComboBox(QWidget *parent = nullptr);
~LtsComboBox() = default;
/**
* @brief Set the default widget to use - either the default QComboBox or the selection widget
* @param bUseSelectionDialog true if the selection widget should be used, false for the normal combobox
*/
static void setUseSelectionDialog(bool bUseSelectionDialog);
/**
* @brief hasValueChanged tells if the value set by default (initialization) or by the user to another value,
* different by the default value. If the user sets the value back to the default value, 'hasValueChanged' will return false again.
* Use this in your UiRequirement to see if value differs from the default value to determine a change.
* @return true if value differs from default value and v.v.
*/
bool hasValueChanged() const;
void setExternalContentType(int ect) { m_externalContentType = ect; }
int externalContentType() const { return m_externalContentType; }
protected:
/**
* @brief showPopup is emited by the native widget when the popup should be triggered (e.g. by clicking a non-writeable LtsComboBox or by clicking the popup-button) and shows an touch-friendly, screen-centered LtsComboBoxPopup
*/
void showPopup() override;
void showEvent(QShowEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *) override;
protected slots:
void userChangedIndex(int index); // user change - for 'hasValueChanged'
signals:
void signalExternallyChanged(); // e.g. PPM's parties changed, ComboBox should report to PpmComboBox for a refill
void signalRefill(); // outgoing
void signalEnterEvent();
void signalLeaveEvent();
private:
int m_currentIndex;
int m_externalContentType;
static bool s_useSelectionDialog;
};
}
File diff suppressed because it is too large. Load diff
+220
View File
@@ -0,0 +1,220 @@
/**
* @RDE_HEADER@
* DTG Edit
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-08-17
*/
#pragma once
//#include "lts_date_time_edit_touch.h"
#include "lts_widgets_export.h"
#include "qcheckbox.h"
#include "qpushbutton.h"
#include <QWidget>
#include <functional>
namespace Ui
{
class LtsDateTimeEditLe; // default, based on QLineEdit
class LtsDateTimeEdit; // touch , based on QSpinBox, QComboBox
}
class QLineEdit;
class QShortcut;
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsDateTimeEdit : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool autoForward READ getAutoForward WRITE setAutoForward)
Q_PROPERTY(bool editTimezone READ getEditTimezone WRITE setEditTimezone)
Q_PROPERTY(bool withButtons READ getWithButtons WRITE setWithButtons)
Q_PROPERTY(bool withSlider READ getWithSlider WRITE setWithSlider)
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
Q_PROPERTY(bool withNowButton READ withNowButton WRITE setWithNowButton)
Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable)
Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY toggled)
public:
// returns the current time in msec
using CurrentTimeCallback = std::function<qint64()>;
explicit LtsDateTimeEdit(QWidget *parent = nullptr, bool withNowButton = true, bool checkable = false);
~LtsDateTimeEdit();
bool setupUiTouch();
qint64 msecsSinceEpoch() const;
bool isEmpty() const;
bool isValidDate() const; // should be preferred over isEmpty()
void setAutoForward(const bool af) { m_autoForward = af; }
bool getAutoForward() const { return m_autoForward; }
void setEditTimezone(const bool edit);
bool getEditTimezone() const { return m_editTimezone; }
void setWithButtons(const bool buttons);
bool getWithButtons() const { return m_withButtons; }
void setWithSlider(const bool slider);
bool getWithSlider() const { return m_withSlider; }
void setReadOnly(const bool readOnly);
bool isReadOnly() const { return m_readOnly; }
void setCheckable(bool checkable);
bool isCheckable() const { return m_checkable; }
void setChecked(bool bChecked);
bool isChecked() const;
void setWithNowButton(bool nowButton);
bool withNowButton() const { return m_withNowButton; }
// Setzt die minimal eingebbare Zeit
void setMinimumTimeMSec(qint64 msec);
qint64 minimumTimeMSec() const { return m_minTimeMSec; }
// Setzt die maximal eingebbare Zeit
void setMaximumTimeMSec(qint64 msec);
qint64 maximumTimeMSec() const { return m_maxTimeMSec; }
void setMinMaxTimeMSec(qint64 min, qint64 max) { setMinimumTimeMSec(min); setMaximumTimeMSec(max); }
void setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb);
bool setTimezone(char tz);
char getTimezone() const;
qint64 getTimezoneAsSecs() const;
void setSubWidgetsProperties(const QString &name, const QVariant &value);
Q_SIGNALS:
void dateTimeChanged();
void toggled(bool on);
public Q_SLOTS:
bool setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible = true);
void clear();
private:
void clearTimezone();
void setMinMaxMonth(int minMonth, int maxMonth);
bool setMonth(int month);
int getMonth() const;
bool setDay(int day);
int getDay() const;
bool setHHMMSS(int hour, int minute, int second);
int getHour() const;
int getMinute() const;
int getSecond() const;
bool setYear(int year);
int getYear() const;
bool isEmptyDay() const;
bool isEmptyHHMMSS() const;
bool isEmptyTimezone() const;
bool isEmptyMonth() const;
bool isEmptyYear() const;
bool hasFocus() const;
bool isDataAcceptable();
void showInvalidData(bool setFocus);
bool isLineEditDataAcceptable(QLineEdit *le) const;
bool isDayAcceptable() const;
bool isHHMMSSAcceptable() const;
bool isMonthAcceptable() const;
bool isYearAcceptable() const;
bool isTzAcceptable() const;
QCheckBox* checkBox() const;
QPushButton* pbNow() const;
private Q_SLOTS:
void setFixedDateTime();
void setOffsetDateTime();
void setSliderValue(int val);
void valueDayChanged(const QString &str);
void fixupHHMMSS();
void valueMonthChanged(const QString &str);
void valueMonthChanged2(const QString &str);
void valueTimezoneChangedStr(const QString &str);
void valueTimezoneChangedInt(int newTimezone);
void valueYearChanged(const QString &str);
void onBackHourClicked();
void onBackMinuteClicked();
void onBackSecondClicked();
void onForwardSecondClicked();
void onForwardMinuteClicked();
void onForwardHourClicked();
void onCheckBoxChanged();
void disableControls();
void enableControls();
void enaDisaControls(bool bEnable);
void onFocusOutHHMMSS();
void onFocusOutTZ();
void onFocusOutDay();
void onFocusOutMonth();
void onFocusOutYear();
void onFocusOutBackspace();
void onFocusOut();
void onHmsWheelEvent(bool bForward, int cursorPos);
void onDayWheelEvent(bool bForward, int cursorPos);
void onMonthWheelEvent(bool bForward, int cursorPos);
void onYearWheelEvent(bool bForward, int cursorPos);
private:
Ui::LtsDateTimeEditLe *m_uiLe{nullptr};
Ui::LtsDateTimeEdit *m_uiTouch{nullptr};
QShortcut *m_scFixed = nullptr;
QShortcut *m_scOffset = nullptr;
CurrentTimeCallback m_getCurrentTimeCallback = nullptr;
qint64 m_minTimeMSec = 0;
qint64 m_maxTimeMSec = 0;
qint64 m_curTimeMSec = -1; // IMMER UTC
bool m_editTimezone = true;
bool m_autoForward = true;
bool m_withButtons = false;
bool m_withSlider = false;
bool m_readOnly = false;
bool m_checkable = false;
bool m_withNowButton = false;
int m_minMonth{0};
int m_maxMonth{0};
bool m_backspacePressed = false;
bool m_recursionAvoid = false;
bool m_dateIsAcceptable = false;
};
} // namespace LtsWidgets
+118
View File
@@ -0,0 +1,118 @@
#include "lts_date_time_edit_p.h"
#include <QApplication>
#include <QRegularExpression>
#include <QValidator>
#include <QKeyEvent>
namespace LtsWidgets
{
//
// LineEdit
//
void LineEdit::clear()
{
setStyleSheet(QString());
QLineEdit::clear();
}
bool LineEdit::isEmpty() const
{
return text().isEmpty();
}
void LineEdit::keyPressEvent(QKeyEvent *event)
{
const bool wasEmpty = isEmpty();
QLineEdit::keyPressEvent(event);
int keyCode = event->key();
if (keyCode == Qt::Key_Backspace && wasEmpty)
{
emit focusOutBackspace();
focusPreviousChild();
event->accept();
}
}
void LineEdit::contextMenuEvent(QContextMenuEvent *e)
{
Q_UNUSED(e);
// damit das Default-Kontextmenü nicht erscheint
}
void LineEdit::focusOutEvent(QFocusEvent *event)
{
QLineEdit::focusOutEvent(event);
emit focusOut();
}
//
// DigitsLineEdit
//
DigitsLineEdit::DigitsLineEdit(QWidget *parent)
: LineEdit(parent)
{
setAlignment(Qt::AlignRight);
QRegularExpression rx(QLatin1String("[-]{0,1}[0-9 ]*"));
setValidator(new QRegularExpressionValidator(rx, this));
}
void DigitsLineEdit::setExactNumDigits(int num)
{
m_exactNumDigits = num;
QRegularExpression rx(QString::asprintf("^[0-9 ]{%d}$", num));
setValidator(new QRegularExpressionValidator(rx, this));
}
void DigitsLineEdit::setValue(int val)
{
QString str;
if (m_exactNumDigits == -1)
str.setNum(val);
else
str = QStringLiteral("%1").arg(val, m_exactNumDigits, 10, QLatin1Char('0'));
setText(str);
}
int DigitsLineEdit::getValue() const
{
if (text().isEmpty())
return 0;
return text().remove(QLatin1Char(' ')).toInt();
}
void DigitsLineEdit::setCursorPosition(int curPos)
{
// WICHTIG: keine negativen Cursorpositionen setzen, da sonst Absturzgefahr !!!
curPos = curPos < 0 ? 1 : curPos;
QLineEdit::setCursorPosition(curPos);
}
void DigitsLineEdit::wheelEvent(QWheelEvent *event)
{
LineEdit::wheelEvent(event);
const QString txt = text();
if (m_exactNumDigits > 0 && txt.length() != m_exactNumDigits)
return;
event->accept();
int cursorPos = cursorPosition();
emit wheelEventReceived(event->delta() > 0, cursorPos);
}
void DigitsLineEdit::keyPressEvent(QKeyEvent *event)
{
LineEdit::keyPressEvent(event);
if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)
{
const bool bUp = event->key() == Qt::Key_Up;
const int cursorPos = cursorPosition();
emit wheelEventReceived(bUp, cursorPos);
}
}
} // namespace LtsWidgets
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <QLineEdit>
namespace LtsWidgets
{
class LineEdit : public QLineEdit
{
Q_OBJECT
public:
using QLineEdit::QLineEdit;
void clear();
bool isEmpty() const;
protected:
void contextMenuEvent(QContextMenuEvent *e) override;
void focusOutEvent(QFocusEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
Q_SIGNALS:
void focusOutBackspace();
void focusOut();
};
class DigitsLineEdit final : public LineEdit
{
Q_OBJECT
public:
DigitsLineEdit(QWidget *parent = nullptr);
void setExactNumDigits(int num);
void setCursorPosition(int curPos);
void setValue(int val);
int getValue() const;
Q_SIGNALS:
void wheelEventReceived(bool bForward, int cursorPos);
protected:
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private:
int m_exactNumDigits = -1;
};
} // namespace LtsWidgets
File diff suppressed because it is too large. Load diff
+213
View File
@@ -0,0 +1,213 @@
/**
* @RDE_HEADER@
* DTG Edit
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-08-17
*/
#pragma once
//#include "lts_date_time_edit_touch.h"
#include "lts_widgets_export.h"
#include <QWidget>
#include <functional>
namespace Ui
{
class LtsDateTimeEditLe; // default, based on QLineEdit
class LtsDateTimeEdit; // touch , based on QSpinBox, QComboBox
}
class QLineEdit;
class QShortcut;
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsDateTimeEdit : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool autoForward READ getAutoForward WRITE setAutoForward)
Q_PROPERTY(bool editTimezone READ getEditTimezone WRITE setEditTimezone)
Q_PROPERTY(bool withButtons READ getWithButtons WRITE setWithButtons)
Q_PROPERTY(bool withSlider READ getWithSlider WRITE setWithSlider)
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
Q_PROPERTY(bool withNowButton READ withNowButton WRITE setWithNowButton)
Q_PROPERTY(bool withCheckBox READ withCheckBox WRITE setWithCheckBox)
public:
// returns the current time in msec
using CurrentTimeCallback = std::function<qint64()>;
explicit LtsDateTimeEdit(QWidget *parent = nullptr, bool withNowButton = true, bool withCheckBox = false);
~LtsDateTimeEdit();
bool setupUiTouch();
qint64 msecsSinceEpoch() const;
bool isEmpty() const;
bool isValidDate() const; // should be preferred over isEmpty()
void setAutoForward(const bool af) { m_autoForward = af; }
bool getAutoForward() const { return m_autoForward; }
void setEditTimezone(const bool edit);
bool getEditTimezone() const { return m_editTimezone; }
void setWithButtons(const bool buttons);
bool getWithButtons() const { return m_withButtons; }
void setWithSlider(const bool slider);
bool getWithSlider() const { return m_withSlider; }
void setReadOnly(const bool readOnly);
bool isReadOnly() const { return m_readOnly; }
void setWithCheckBox(bool checkBoxEnabled);
bool withCheckBox() const { return m_withCheckBox; }
void setCheckBoxChecked(bool bChecked);
bool isCheckBoxChecked() const;
void setWithNowButton(bool nowButton);
bool withNowButton() const { return m_withNowButton; }
// Setzt die minimal eingebbare Zeit
void setMinimumTimeMSec(qint64 msec);
qint64 minimumTimeMSec() const { return m_minTimeMSec; }
// Setzt die maximal eingebbare Zeit
void setMaximumTimeMSec(qint64 msec);
qint64 maximumTimeMSec() const { return m_maxTimeMSec; }
void setMinMaxTimeMSec(qint64 min, qint64 max) { setMinimumTimeMSec(min); setMaximumTimeMSec(max); }
void setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb);
bool setTimezone(char tz);
char getTimezone() const;
qint64 getTimezoneAsSecs() const;
void setSubWidgetsProperties(const QString &name, const QVariant &value);
Q_SIGNALS:
void dateTimeChanged();
public Q_SLOTS:
bool setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible = true);
void clear();
private:
void clearTimezone();
void setMinMaxMonth(int minMonth, int maxMonth);
bool setMonth(int month);
int getMonth() const;
bool setDay(int day);
int getDay() const;
bool setHHMMSS(int hour, int minute, int second);
int getHour() const;
int getMinute() const;
int getSecond() const;
bool setYear(int year);
int getYear() const;
bool isEmptyDay() const;
bool isEmptyHHMMSS() const;
bool isEmptyTimezone() const;
bool isEmptyMonth() const;
bool isEmptyYear() const;
bool hasFocus() const;
bool isDataAcceptable();
void showInvalidData(bool setFocus);
bool isLineEditDataAcceptable(QLineEdit *le) const;
bool isDayAcceptable() const;
bool isHHMMSSAcceptable() const;
bool isMonthAcceptable() const;
bool isYearAcceptable() const;
bool isTzAcceptable() const;
private Q_SLOTS:
void setFixedDateTime();
void setOffsetDateTime();
void setSliderValue(int val);
void valueDayChanged(const QString &str);
void fixupHHMMSS();
void valueMonthChanged(const QString &str);
void valueMonthChanged2(const QString &str);
void valueTimezoneChangedStr(const QString &str);
void valueTimezoneChangedInt(int newTimezone);
void valueYearChanged(const QString &str);
void onBackHourClicked();
void onBackMinuteClicked();
void onBackSecondClicked();
void onForwardSecondClicked();
void onForwardMinuteClicked();
void onForwardHourClicked();
void onCheckBoxChanged();
void disableControls();
void enableControls();
void enaDisaControls(bool bEnable);
void onFocusOutHHMMSS();
void onFocusOutTZ();
void onFocusOutDay();
void onFocusOutMonth();
void onFocusOutYear();
void onFocusOutBackspace();
void onFocusOut();
void onHmsWheelEvent(bool bForward, int cursorPos);
void onDayWheelEvent(bool bForward, int cursorPos);
void onMonthWheelEvent(bool bForward, int cursorPos);
void onYearWheelEvent(bool bForward, int cursorPos);
private:
Ui::LtsDateTimeEditLe *m_uiLe{nullptr};
Ui::LtsDateTimeEdit *m_uiTouch{nullptr};
QShortcut *m_scFixed = nullptr;
QShortcut *m_scOffset = nullptr;
CurrentTimeCallback m_getCurrentTimeCallback = nullptr;
qint64 m_minTimeMSec = 0;
qint64 m_maxTimeMSec = 0;
qint64 m_curTimeMSec = -1; // IMMER UTC
bool m_editTimezone = true;
bool m_autoForward = true;
bool m_withButtons = false;
bool m_withSlider = false;
bool m_readOnly = false;
bool m_withCheckBox = false;
bool m_withNowButton = false;
int m_minMonth{0};
int m_maxMonth{0};
bool m_backspacePressed = false;
bool m_recursionAvoid = false;
bool m_dateIsAcceptable = false;
};
} // namespace LtsWidgets
+396
View File
@@ -0,0 +1,396 @@
/**
* @RDE_HEADER@
* Edit which contains some widgets to edit each: Date + Timezone + Time
* $Id$
* $HeadURL$
* $Author$
*
* Created by Patric Karwat, NLE, 2019-06-27
*/
#include "lts_date_time_edit_touch.h"
#include "ui_lts_date_time_edit_touch.h"
#include <ngltools/ngltools_dtg.h>
#include <LtsSession/lts_session_data.h>
#include <LtsUtilsSettings/settings.h>
#include <QShortcut>
namespace LtsWidgets
{
LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool withCheckBox)
: QWidget(parent)
, m_withCheckBox(withCheckBox)
, m_withNowButton(withNowButton)
, m_tzOffset(0)
, m_minMonth(1)
, m_maxMonth(12)
, m_scFixed(new QShortcut(QKeySequence(Qt::ShiftModifier + Qt::Key_F2), this, SLOT(onSetFixedDateTime()), nullptr, Qt::WidgetWithChildrenShortcut))
, m_scOffset(new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(onSetOffsetDateTime()), nullptr, Qt::WidgetWithChildrenShortcut))
, m_getCurrentTimeCallback(nullptr)
, m_ui(new Ui::LtsDateTimeEdit)
{
m_ui->setupUi(this);
setFocusPolicy(Qt::StrongFocus);
for (int i = 65; i < 91; i++ )
{
if (QChar(i).toUpper() == QChar::fromLatin1('I'))
continue;
m_ui->cbxTimeZone->addItem(QChar(i), QChar(i));
}
connect(m_ui->pbNow, &QPushButton::clicked,
this, &LtsDateTimeEdit::onSetOffsetDateTime);
connect(m_ui->cbxMonth, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &LtsDateTimeEdit::onDateTimeChanged);
connect(m_ui->spDay, &QSpinBox::editingFinished,
this, &LtsDateTimeEdit::onDateTimeChanged);
connect(m_ui->spHour, &QSpinBox::editingFinished,
this, &LtsDateTimeEdit::onDateTimeChanged);
connect(m_ui->spMinutes, &QSpinBox::editingFinished,
this, &LtsDateTimeEdit::onDateTimeChanged);
connect(m_ui->spSeconds, &QSpinBox::editingFinished,
this, &LtsDateTimeEdit::onDateTimeChanged);
connect(m_ui->spYear, &QSpinBox::editingFinished,
this, &LtsDateTimeEdit::onDateTimeChanged);
connect(m_ui->checkBox, &QCheckBox::toggled,
this, &LtsDateTimeEdit::onCheckBoxChanged);
connect(m_ui->cbxTimeZone, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &LtsDateTimeEdit::onTimeZoneChanged);
connect(m_ui->cbxMonth, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &LtsDateTimeEdit::onMonthChanged);
m_tzOffset = DTG::timezone2Secs(QLatin1Char(DTG::timezone()).toLatin1());
m_ui->cbxTimeZone->setCurrentIndex(m_ui->cbxTimeZone->findText(QString(QLatin1Char(DTG::timezone()))));
onTimeZoneChanged();
setMinMaxMonth(1,12);
//m_ui->pbNow->setMaximumHeight(m_ui->dteLeft->height());
int offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong()/1000;
m_ui->pbNow->setToolTip(tr("Set to current time plus %n second(s) offset", "", offset));
updateDateTimeType();
m_ui->checkBox->setVisible(m_withCheckBox);
m_ui->pbNow->setVisible(m_withNowButton);
}
LtsDateTimeEdit::~LtsDateTimeEdit()
{
delete m_ui;
}
void LtsDateTimeEdit::updateDateTimeType()
{
switch (DTG::dateTimeType())
{
case DTG::DateTimeType::DTG:
break;
case DTG::DateTimeType::DDMMYYYY_HHMMSS_Z:
break;
case DTG::DateTimeType::DDMMYYYY_Z_HHMMSS:
break;
default:
break;
}
}
bool LtsDateTimeEdit::setTimezone(char tz)
{
QChar tzLocal = QChar(QLatin1Char(tz)).toUpper();
if (tzLocal.toLatin1() == 'J')
tzLocal = QLatin1Char('Z');
m_ui->cbxTimeZone->setCurrentIndex(m_ui->cbxTimeZone->findData(tzLocal));
int offset = DTG::timezone2Secs(tzLocal.toLatin1());
if (offset != m_tzOffset)
{
const auto ms = msecsSinceEpoch();
m_tzOffset = offset;
setMsecsSinceEpoch(ms+m_tzOffset);
}
return true;
}
char LtsDateTimeEdit::getTimezone() const
{
QChar tz = m_ui->cbxTimeZone->currentText().at(0).toUpper();
// if (tz.isLower())
// tz = tz.toUpper();
if (tz.toLatin1() == 'J')
tz = QLatin1Char('Z');
if (tz.isNull())
return DTG::timezone();
return tz.toLatin1();
}
void LtsDateTimeEdit::setMinMaxMonth(int minMonth, int maxMonth )
{
if (m_minMonth != minMonth)
m_minMonth = minMonth;
if (m_maxMonth != maxMonth)
m_maxMonth = maxMonth;
int currentMonth = m_ui->cbxMonth->currentData().toInt();
m_ui->cbxMonth->blockSignals(true);
m_ui->cbxMonth->clear();
for (int i =1; i<= 12 ; i++)
{
if (i < m_minMonth)
continue;
if (i > m_maxMonth)
break;
QDate date(1777,i,1);
m_ui->cbxMonth->addItem(date.toString(QStringLiteral("MMM")),i);
}
m_ui->cbxMonth->setCurrentIndex(m_ui->cbxMonth->findData(currentMonth));
m_ui->cbxMonth->blockSignals(false);
}
void LtsDateTimeEdit::setDateTime(const QDateTime &datetime)
{
if (!datetime.isValid() && m_withCheckBox)
{
m_ui->checkBox->setChecked(false);
return;
}
QDateTime dt = datetime.toUTC().addSecs(m_tzOffset);
dt.setOffsetFromUtc(m_tzOffset);
//QDateTime oldDateTime = m_dateTime; // Keep to emit signal only when actually changed! (i.e. non-active or out of bounds)
QDateTime oldDateTime = dateTime();
if (dt == oldDateTime)
return;
blockSignals(true); // don't emit dateTimeChanged() twice!
m_ui->spDay->setValue(dt.date().day()); //day
m_ui->cbxMonth->setCurrentIndex(m_ui->cbxMonth->findData(dt.date().month())); //month
// year
//if (m_ui->spYear->maximum() > 1000)
// m_ui->spYear->setValue(dt.date().year());
//else
m_ui->spYear->setValue(dt.date().year()-2000);
m_ui->spHour->setValue(dt.time().hour());
m_ui->spMinutes->setValue(dt.time().minute());
m_ui->spSeconds->setValue(dt.time().second());
blockSignals(false);
const QDateTime dtNew = dateTime();
if (dtNew != oldDateTime)
emit dateTimeChanged();
}
void LtsDateTimeEdit::onDateTimeChanged()
{
emit dateTimeChanged();
QDateTime dateTime;
dateTime = dateTime.addYears(m_ui->spYear->value());
dateTime = dateTime.addMonths(m_ui->cbxMonth->currentData().toInt());
dateTime = dateTime.addDays(m_ui->spDay->value());
dateTime = dateTime.addSecs(m_ui->spHour->value()*60*60 + m_ui->spMinutes->value()*60 + m_ui->spSeconds->value());
if (dateTime.isValid())
{
m_dateTime = dateTime;
// emit dateTimeChanged(m_dateTime);
}
}
void LtsDateTimeEdit::onTimeZoneChanged()
{
if (m_ui->cbxMonth->currentText().isEmpty())
{
return;
}
QChar tz = m_ui->cbxTimeZone->currentText().at(0);
if (tz.isLower())
tz = tz.toUpper();
if (tz.toLatin1() == 'J')
tz = QLatin1Char('Z');
m_ui->cbxTimeZone->setCurrentIndex(m_ui->cbxTimeZone->findData(tz));
int offset = DTG::timezone2Secs(tz.toLatin1());
if (offset != m_tzOffset)
{
const auto ms = msecsSinceEpoch();
m_tzOffset = offset;
setMsecsSinceEpoch(ms+m_tzOffset);
}
}
void LtsDateTimeEdit::onMonthChanged(int month)
{
// TODO
Q_UNUSED(month);
}
void LtsDateTimeEdit::setWithCheckBox(bool checkBoxEnabled)
{
if (m_withCheckBox == checkBoxEnabled)
return;
m_withCheckBox = checkBoxEnabled;
m_ui->checkBox->setVisible(m_withCheckBox);
if (!m_withCheckBox) // If no checkBox -> set other Elements enabled
m_ui->checkBox->setChecked(true);
emit withCheckBoxChanged(m_withCheckBox);
}
bool LtsDateTimeEdit::isCheckBoxChecked() const
{
return m_ui->checkBox->isChecked();
}
void LtsDateTimeEdit::setWithNowButton(bool nowButton)
{
if (m_withNowButton == nowButton)
return;
m_withNowButton = nowButton;
m_ui->pbNow->setVisible(m_withNowButton);
emit withNowButtonChanged(m_withNowButton);
}
void LtsDateTimeEdit::onCheckBoxChanged(bool checked)
{
m_ui->spDay->setEnabled(checked);
m_ui->spHour->setEnabled(checked);
m_ui->spMinutes->setEnabled(checked);
m_ui->spSeconds->setEnabled(checked);
m_ui->spYear->setEnabled(checked);
m_ui->cbxMonth->setEnabled(checked);
m_ui->cbxTimeZone->setEnabled(checked);
m_ui->pbNow->setEnabled(checked);
emit dateTimeChanged();
}
void LtsDateTimeEdit::setMsecsSinceEpoch(qint64 mSecsSinceEpoch)
{
if (mSecsSinceEpoch == 0 && m_withCheckBox)
{
m_ui->checkBox->setChecked(false);
return;
}
m_ui->checkBox->setChecked(true);
setDateTime(QDateTime::fromMSecsSinceEpoch(mSecsSinceEpoch).toUTC());
}
qint64 LtsDateTimeEdit::msecsSinceEpoch() const
{
const QDateTime dt(dateTime());
if (dt.isNull())
return 0;
return dt.toMSecsSinceEpoch();
}
void LtsDateTimeEdit::clear()
{
m_ui->spDay->clear();
m_ui->spHour->clear();
m_ui->spMinutes->clear();
m_ui->spSeconds->clear();
m_ui->spYear->clear();
emit dateTimeChanged();
}
void LtsDateTimeEdit::setCheckBoxChecked(bool bChecked)
{
m_ui->checkBox->setChecked(bChecked);
}
QDateTime LtsDateTimeEdit::dateTime() const
{
if (!m_ui->checkBox->isChecked())
return QDateTime();
QDateTime final;
int year = 0;
if (m_ui->spYear->value() < 1000)
{
year=m_ui->spYear->value()+2000;
}
else
{
year=m_ui->spYear->value();
}
QDate date(year,m_ui->cbxMonth->currentData().toInt(), m_ui->spDay->value());
final.setDate(date);
final = final.addSecs(m_ui->spHour->value()*60*60 + m_ui->spMinutes->value()*60 + m_ui->spSeconds->value());
if (!final.isValid())
return QDateTime();
// Todo: Enhance?
return QDateTime(final.date(), final.time(), Qt::OffsetFromUTC, m_tzOffset).toUTC();
}
void LtsDateTimeEdit::setMaximumTimeMSec(qint64 msec)
{
const auto maximumDate(QDateTime::fromMSecsSinceEpoch(msec).date());
m_ui->spYear->setMaximum(maximumDate.year()-2000);
//m_ui->spDay->setMaximum(maximumDate.day());
// setMinMaxMonth(maximumDate.month(), m_maxMonth);
}
qint64 LtsDateTimeEdit::minimumTimeMSec() const
{
QDate date;
date.setDate(m_ui->spYear->minimum(), m_maxMonth, m_ui->spMinutes->minimum() );
return QDateTime(date).toMSecsSinceEpoch();
}
void LtsDateTimeEdit::setMinimumTimeMSec(qint64 msec)
{
const auto minimumDate(QDateTime::fromMSecsSinceEpoch(msec).date());
//setMinMaxMonth(minimumDate.month(), m_maxMonth);
m_ui->spYear->setMinimum(minimumDate.year()-2000);
//m_ui->spDay->setMinimum(minimumDate.day());
}
void LtsDateTimeEdit::setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb)
{
m_getCurrentTimeCallback = cb;
}
void LtsDateTimeEdit::onSetFixedDateTime()
{
setMsecsSinceEpoch(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("FixedTime"), 0).toLongLong());
}
void LtsDateTimeEdit::onSetOffsetDateTime()
{
if (!m_getCurrentTimeCallback)
return;
qint64 offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong();
// we only show seconds, so strip away milliseconds
setMsecsSinceEpoch((m_getCurrentTimeCallback() / 1000) * 1000 + offset);
}
void LtsDateTimeEdit::setSubWidgetsProperties(QString name, QVariant value)
{
m_ui->spDay->setProperty(name.toStdString().c_str(), value);
m_ui->spHour->setProperty(name.toStdString().c_str(), value);
m_ui->spMinutes->setProperty(name.toStdString().c_str(), value);
m_ui->spSeconds->setProperty(name.toStdString().c_str(), value);
m_ui->spYear->setProperty(name.toStdString().c_str(), value);
}
} //namespace
+147
View File
@@ -0,0 +1,147 @@
/**
* @RDE_HEADER@
* Edit which contains some widgets to edit each: Date + Timezone + Time
* $Id$
* $HeadURL$
* $Author$
*
* Created by Patric Karwat, NLE, 2019-06-27
*/
#pragma once
#include "lts_widgets_export.h"
#include <QDate>
#include <QWidget>
#include <functional>
class QDateTime;
namespace Ui
{
class LtsDateTimeEdit;
}
class QShortcut;
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsDateTimeEdit : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool autoForward READ getAutoForward WRITE setAutoForward)
Q_PROPERTY(bool editTimezone READ getEditTimezone WRITE setEditTimezone)
Q_PROPERTY(bool withButtons READ getWithButtons WRITE setWithButtons)
Q_PROPERTY(bool withSlider READ getWithSlider WRITE setWithSlider)
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
Q_PROPERTY(bool withNowButton READ withNowButton WRITE setWithNowButton)
Q_PROPERTY(bool withCheckBox READ withCheckBox WRITE setWithCheckBox)
public:
using CurrentTimeCallback = std::function<qint64()>;
explicit LtsDateTimeEdit(QWidget *parent = nullptr, bool withNowButton = true, bool withCheckBox = false);
~LtsDateTimeEdit();
qint64 msecsSinceEpoch() const;
bool isEmpty() const { return msecsSinceEpoch() == 0; }
void setAutoForward(const bool af) { m_autoForward = af; }
bool getAutoForward() const { return m_autoForward; }
void setEditTimezone(const bool edit) { m_editTimezone = edit; }
bool getEditTimezone() const { return m_editTimezone; }
void setWithButtons(const bool buttons) { m_withButtons = buttons; }
bool getWithButtons() const { return m_withButtons; }
void setWithSlider(const bool slider) { m_withSlider = slider; }
bool getWithSlider() const { return m_withSlider; }
void setReadOnly(const bool readOnly) { m_readOnly = readOnly; }
bool isReadOnly() const { return m_readOnly; }
bool withCheckBox() const { return m_withCheckBox; }
void setCheckBoxChecked(bool bChecked);
bool isCheckBoxChecked() const;
bool withNowButton() const { return m_withNowButton; }
void setDateTime(const QDateTime &datetime);
// Setzt die minimal eingebbare Zeit
void setMinimumTimeMSec(qint64 msec);
qint64 minimumTimeMSec() const;
// Setzt die maximal eingebbare Zeit
void setMaximumTimeMSec(qint64 msec);
qint64 maximumTimeMSec() const { return m_maxTimeMSec; };
void setMinMaxTimeMSec(qint64 min, qint64 max) { setMinimumTimeMSec(min); setMaximumTimeMSec(max); }
void setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb);
void updateDateTimeType();
bool setTimezone(char tz);
char getTimezone() const;
void setSubWidgetsProperties(QString name, QVariant value);
bool isValidDate() const
{
//Face validator for touch.
return m_dateTime.isValid();
}
private:
QDateTime dateTime() const;
void setMinMaxMonth(int minMonth, int maxMonth);
public slots:
void setWithCheckBox(bool checkBoxEnabled);
void setWithNowButton(bool nowButton);
void setMsecsSinceEpoch(qint64 mSecsSinceEpoch);
void clear();
void onSetFixedDateTime();
void onSetOffsetDateTime();
public slots:
void onDateTimeChanged();
signals:
void dateTimeChanged();
void withCheckBoxChanged(bool checkBoxEnabled);
void withNowButtonChanged(bool nowButton);
private slots:
void onCheckBoxChanged(bool checked);
void onTimeZoneChanged();
void onMonthChanged(int month);
private:
qint64 m_minTimeMSec = 0;
qint64 m_maxTimeMSec = 0;
qint64 m_curTimeMSec = -1;
bool m_editTimezone = true;
bool m_autoForward = true;
bool m_withButtons = false;
bool m_withSlider = false;
bool m_readOnly = false;
bool m_withCheckBox = false;
bool m_withNowButton = false;
int m_tzOffset; // in secs
int m_minMonth;
int m_maxMonth;
QShortcut *m_scFixed;
QShortcut *m_scOffset;
QDateTime m_dateTime;
CurrentTimeCallback m_getCurrentTimeCallback;
Ui::LtsDateTimeEdit *m_ui;
};
} //namespace LtsWidgets
+181
View File
@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsDateTimeEdit</class>
<widget class="QWidget" name="LtsDateTimeEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>558</width>
<height>31</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>558</width>
<height>31</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="dtgLayout">
<item>
<widget class="QCheckBox" name="checkBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spDay">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>31</number>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="minimumSize">
<size>
<width>5</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spHour">
<property name="maximum">
<number>23</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spMinutes">
<property name="maximum">
<number>59</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spSeconds">
<property name="maximum">
<number>59</number>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="minimumSize">
<size>
<width>5</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxTimeZone">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="minimumSize">
<size>
<width>5</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxMonth">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spYear">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>99</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbNow">
<property name="text">
<string>Now</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+819
View File
@@ -0,0 +1,819 @@
/**
* @RDE_HEADER@
* Dialog for the new Layout system
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_dialog.h"
#include "lts_progress_dialog_helper.h"
#include "lts_message_dialog.h"
#include "lts_dialog_helper.h"
#include "lts_dialog_button_box.h"
#include "lts_base_layout_widget.h"
#include "lts_date_time_edit.h"
#include <QHBoxLayout>
#include <QProgressBar>
#include <QLineEdit>
#include <QComboBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QDateTimeEdit>
#include <QSlider>
#include <QPlainTextEdit>
#include <QTextEdit>
#include <QWidget>
#include <QRadioButton>
#include <QTableView>
#include <QTableWidget>
#include <QListView>
#include <QListWidget>
#include <QSlider>
#include <QScroller>
#include <QScrollArea>
#include <QMenu>
#include <QInputDialog>
#include <QHBoxLayout>
//#include <QGraphicsBlurEffect>
#include <QPainter>
#include <QColor>
#include <QMetaMethod>
#include <QDebug>
#include <LtsSession/lts_session_data.h>
namespace LtsWidgets
{
int LtsDialog::s_object_count = 0;
class OverlayContainer : public QWidget
{
Q_OBJECT
public:
OverlayContainer(QWidget *parent);
protected:
void paintEvent(QPaintEvent *e) override;
};
OverlayContainer::OverlayContainer(QWidget *parent)
: QWidget(parent)
{
setAttribute(Qt::WA_TranslucentBackground);
}
void OverlayContainer::paintEvent(QPaintEvent *e)
{
QPainter p(this);
p.fillRect(rect(), { 80, 80, 80, 200 });
QWidget::paintEvent(e);
}
// LtsDialog
LtsDialog::LtsDialog(QWidget *parent, ButtonCombination buttonCombination, bool closeable)
: QWidget(/*parent*/)
, m_changesMade(false)
, m_firstShow(true)
, m_baseLayoutWidget(nullptr)
, m_isClosable(closeable)
, m_noLockTopic(false)
, m_unchangeableDialog(false)
, m_dialogHelper(new LtsDialogHelper(nullptr)) // has no parent, but LtsTabWidget sets it to itself
//, m_progressDialogHelper(new LtsProgressDialogHelper(this))
// protected
, m_buttonBox(nullptr)
, m_uiRequirements(new UiRequirementList())
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsWidget.LtsDialog"))
{
Q_UNUSED(parent);
s_object_count++;
m_dialogHelper->setDialog(this); // shadow this dialog on the helper class
m_buttonBox = m_dialogHelper->getButtonBox(); // for new dialogs, so they can connect to it
// connect(this, &QObject::destroyed,
// new QObject, [this]()
// {
// qWarning() << (qintptr)this << "LtsDialog destroyed";
// });
// connect(m_dialogHelper, &QObject::destroyed,
// new QObject, [this]()
// {
// qWarning() << (qintptr)m_dialogHelper.data() << "LtsDialogHelper destroyed";
// });
addDebugMenuButton();
/*
OkApplyCancel, // Ok saves changes, closes the Dialog - Apply only saves changes, does not close the Dialog - Cancel closes the Dialog if there are no changes made
OkCancel, // Special case: only use if you are 100% sure what this is => example: New Message: there is only a SEND button, if sent, the dialog MUST be closed
CloseOnly, // For Dialogs that do not save anything
SeamlessDialog, // No buttons, no margins!
*/
connect(m_buttonBox->getButton(DialogButton::Close), &QPushButton::clicked, this, &LtsDialog::slotClose); // is (almost) always shown
switch(buttonCombination)
{
case ButtonCombination::OkCancel:
{
m_buttonBox->getButton(DialogButton::Close)->setText(tr("Cancel")); // kb den button enum zu "cancel" umzubenennen
connect(m_buttonBox->getButton(DialogButton::Ok), &QPushButton::clicked, this, &LtsDialog::slotOkay);
break;
}
case ButtonCombination::OkApplyCancel:
{
m_buttonBox->getButton(DialogButton::Close)->setText(tr("Cancel")); // kb den button enum zu "cancel" umzubenennen
connect(m_buttonBox->getButton(DialogButton::Ok), &QPushButton::clicked, this, &LtsDialog::slotOkay);
connect(m_buttonBox->getButton(DialogButton::Apply), &QPushButton::clicked, this, &LtsDialog::slotApply);
m_buttonBox->getButton(DialogButton::Apply)->setVisible(true);
break;
}
case ButtonCombination::CloseOnly:
{
m_buttonBox->getButton(DialogButton::Ok)->setVisible(false);
setUnchangeable();
break;
}
default:
case ButtonCombination::SeamlessDialog:
{
m_buttonBox->setVisible(false);
setContentsMargins(0,0,0,0);
m_dialogHelper->setContentsMargins(0,0,0,0);
QLayout *l = m_dialogHelper->layout();
if (l)
{
l->setContentsMargins(0,0,0,0);
l->setMargin(0);
}
else
{
qWarning() << "LtsDialog::c_tor: Dialog Helper has no layout to set margins";
}
break;
}
}
if (!closeable)
{
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setVisible(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Close)->setVisible(false);
}
}
LtsDialog::~LtsDialog()
{
delete m_uiRequirements;
if (m_overlayWidget)
m_overlayWidget->setParent(nullptr);
// do not delete the dialog helper, as the dialog helper deletes this class
// also do not delete, as it is parented! => delete m_progressDialogHelper;
s_object_count--;
}
void LtsDialog::setOverlay(QWidget *widget)
{
if (m_overlayContainer)
{
if (m_overlayWidget)
{
m_overlayWidget->hide();
m_overlayWidget->setParent(nullptr);
m_overlayWidget->hide();
m_overlayWidget = nullptr;
// NOTE: we didn't create the widget so we don't delete it
}
m_overlayContainer->hide();
m_overlayContainer->setParent(nullptr);
m_overlayContainer->hide();
m_overlayContainer->deleteLater();
m_overlayContainer = nullptr;
}
// add a cool transparent background as an overlay to block the underlying dialog from user input, etc.
OverlayContainer *overlayContainer = new OverlayContainer(m_dialogHelper); // TODO: check race condition with dialogHelper's deletion / unparenting
connect(m_dialogHelper.data(), &LtsDialogHelper::signalSizeChanged,
overlayContainer, [overlayContainer](const QSize &size)
{
overlayContainer->setGeometry(QRect(QPoint(0, 0), size));
});
overlayContainer->setGeometry(QRect(QPoint(0, 0), m_dialogHelper->size()));
m_overlayContainer = overlayContainer;
QHBoxLayout *l = new QHBoxLayout;
m_overlayContainer->setLayout(l);
widget->setAutoFillBackground(true);
l->addWidget(widget, 0, Qt::AlignCenter);
widget->show();
connect(widget, &QWidget::destroyed,
m_overlayContainer.data(), &QObject::deleteLater); // every widget has its own container, so deleteLater is okay here
m_overlayWidget = widget;
m_overlayContainer->show();
m_overlayContainer->raise();
}
void LtsDialog::unsetOverlay()
{
if (m_overlayWidget)
m_overlayWidget->setParent(nullptr);
if (!m_overlayContainer)
return;
m_overlayContainer->setParent(nullptr);
m_overlayContainer->deleteLater();
}
LtsDialogHelper *LtsDialog::getHelper()
{
return m_dialogHelper;
}
LtsBaseLayoutWidget* LtsDialog::baseLayoutWidget()
{
return m_baseLayoutWidget;
}
void LtsDialog::setOnlineHelpFilename(const QString &fileName) // string without file extension !!
{
m_onlineHelpFileName = fileName;
QPushButton *btn = m_buttonBox->getButton(DialogButton::Help);
if (btn == nullptr) // paranoid
return;
if (LtsSessionData::LtsSessionData::getInstance()->isTablet() && strcmp(PROJECTBUILD, "guez") == 0)
return; //no more help in Guez OC
connect(btn, &QPushButton::clicked,
this, [this]()
{
emit signalShowOnlineHelp(m_onlineHelpFileName);
});
btn->setVisible(true);
}
void LtsDialog::addDebugMenuButton()
{
#ifdef LTS_DEBUG // debug menu
QMenu *menu = new QMenu(this);
menu->addAction(QStringLiteral("simulate changes"), this, &LtsDialog::slotChangesMade);
menu->addAction(QStringLiteral("unchange"), this, &LtsDialog::__unchange__);
menu->addAction(QStringLiteral("retranslate"), this, &LtsDialog::retranslate);
menu->addAction(QStringLiteral("force unlock"), this, [this]() { setUnchangeable(false); });
menu->addAction(QStringLiteral("set unchangeable"), this, [this]() { setUnchangeable(); });
menu->addAction(QStringLiteral("try lock"), this, &LtsDialog::tryLockTopic);
menu->addAction(QStringLiteral("set closable"), this, [this]() { m_isClosable = true; });
menu->addAction(QStringLiteral("set unclosable"), this, [this]() { m_isClosable = false; });
menu->addAction(QStringLiteral("clear requirements"), this, [this] { m_uiRequirements->clear(); });
menu->addAction(QStringLiteral("enable all button box buttons"), this, [this]()
{
const auto children = m_buttonBox->findChildren<QPushButton *>();
for (QPushButton *pb : children)
pb->setEnabled(true);
});
menu->addAction(QStringLiteral("show button box button .."), this, [this]()
{
QString buttonObjectName = QInputDialog::getText(this, QString(), QStringLiteral("Enter button object name"));
if (buttonObjectName.isEmpty())
return;
const QList<QPushButton *> buttons = m_buttonBox->findChildren<QPushButton *>(buttonObjectName);
int btnCount = buttons.count();
if (btnCount == 0)
{
LtsMessageDialog::showMessage(this, QStringLiteral("button not found"));
return;
}
if (btnCount > 1)
{
QStringList buttonStringList;
buttonStringList.reserve(buttons.size());
for (QPushButton *pb : buttons)
buttonStringList += pb->objectName();
LtsMessageDialog::showMessage(this, QStringLiteral("button ambiguous: %1").arg(buttonStringList.join(QStringLiteral(", "))));
return;
}
buttons.at(0)->setVisible(true);
});
QPushButton *debugBtn = m_buttonBox->getButton(DialogButton::_debug_);
debugBtn->setMenu(menu);
debugBtn->setVisible(true);
#endif
}
void LtsDialog::updateWindowTitle()
{
QString completeTitle = m_title;
if (m_changesMade)
completeTitle.insert(0, QStringLiteral("\u2022")); // add modified-indicator unicode 'bullet'
if (!m_topicLockedBy.isEmpty())
completeTitle.push_back(QString(QStringLiteral(" - READ-ONLY (%1)")).arg(m_topicLockedBy));
QWidget::setWindowTitle(completeTitle);
if (m_dialogHelper != nullptr)
m_dialogHelper->setWindowTitle(completeTitle);
}
void LtsDialog::setWindowTitle(const QString &title)
{
m_title = title;
updateWindowTitle();
}
void LtsDialog::setBaseLayoutWidget(LtsBaseLayoutWidget *baseLayoutWidget)
{
m_baseLayoutWidget = baseLayoutWidget;
}
void LtsDialog::setIdentifier(const QString &identifier)
{
m_identifier = identifier;
tableColumnOrganiserSetting();
}
void LtsDialog::addConnections(QWidget *widget)
{
if (addChangesMadeSignal<QLineEdit>(widget, &QLineEdit::textChanged)) return;
if (addChangesMadeSignal<QDateTimeEdit>(widget, &QDateTimeEdit::dateTimeChanged)) return;
if (addChangesMadeSignal<QComboBox>(widget, &QComboBox::currentTextChanged)) return;
if (addChangesMadeSignal<QCheckBox>(widget, &QCheckBox::stateChanged)) return;
if (addChangesMadeSignal<QSpinBox>(widget, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged))) return;
if (addChangesMadeSignal<QDoubleSpinBox>(widget, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged))) return;
if (addChangesMadeSignal<QSlider>(widget, &QSlider::valueChanged)) return;
if (addChangesMadeSignal<QPlainTextEdit>(widget, &QPlainTextEdit::textChanged)) return;
if (addChangesMadeSignal<QTextEdit>(widget, &QTextEdit::textChanged)) return; // TODO: rm - plain text only!
if (addChangesMadeSignal<QAbstractButton>(widget, &QAbstractButton::toggled)) return;
if (addChangesMadeSignal<LtsDateTimeEdit>(widget, &LtsDateTimeEdit::dateTimeChanged)) return;
}
void LtsDialog::setUnchangeable(bool unchangeable) // adjust structure to new functionality (NOTE: calling this twice without restoring will currently result in bad behaviour)
{
m_unchangeableDialog = unchangeable;
}
bool LtsDialog::addDialogFixed(LtsDialog *dialog, const char *menuGroupUntranslated, const QString &identifier, const LayoutArea &layoutArea, bool setClosable, bool focusNewTab)
{
return addDialog(dialog, /* >> */ tr(menuGroupUntranslated) /* << */, identifier, layoutArea, setClosable, focusNewTab);
}
bool LtsDialog::addDialog(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea, bool setClosable, bool focusNewTab)
{
if (!m_baseLayoutWidget)
{
qCritical() << "LtsDialog: addDialog: no base layout set - cannot add Dialog !";
return false;
}
return m_baseLayoutWidget->addWidget(dialog, menuGroup, identifier, layoutArea, setClosable, focusNewTab);
}
bool LtsDialog::amIFocused() const
{
if (!m_baseLayoutWidget)
{
qCritical() << "LtsDialog: addDialog: no base layout set - cannot check for focus!";
return false;
}
return m_baseLayoutWidget->isDialogFocused(this);
}
bool LtsDialog::focusDialog(const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea)
{
if (!m_baseLayoutWidget)
{
qCritical() << "LtsDialog: addDialog: no base layout set - cannot focus Dialog !";
return false;
}
return m_baseLayoutWidget->focusTab(menuGroup, identifier, layoutArea);
}
void LtsDialog::showEvent(QShowEvent *event)
{
emit visibilityChanged(true);
bool tmp = m_unchangeableDialog;
if (!m_firstShow) // do not reconnect if already connected (and do nothing if it's an unchangeable dialog)
{
QWidget::showEvent(event);
return;
}
m_unchangeableDialog = true; // do not monitor changes, yet
m_firstShow = false;
// the fact that all widgets are created before a window is shown, lets us connect to all editable widgets and listen to changes
// TODO: mit chris drueber reden ;) evtl. event listener, im widget event abfangen oder an ganz anderer stelle ?
QList<QWidget *> listenWidgets = findChildren<QWidget *>();
for (auto i = listenWidgets.begin(); i != listenWidgets.end(); ++i)
{
// TODO: more widgets
// TODO: NGLPositionEdit / LtsPositionEdit
QWidget *widget = *i;
QVariant isConst = widget->property("const");
if (isConst.isValid() && isConst.toBool())
continue;
if (!tmp)
addConnections(widget);
#ifdef TABLET_MODE
LtsWidgets::addTouchScroll(widget);
#endif
}
QWidget::showEvent(event);
m_unchangeableDialog = tmp; // start monitoring changes only after this point, as user changes may now be made
}
void LtsDialog::tryLockTopic()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!m_topicInfo.isValid())
return;
if (m_topicInfo.isLocked())
{
WARNING_LOG(m_logger) << LOGFUNCTION << " tried to lock with an already locked topic info, return";
return;
}
if (m_noLockTopic)
return;
// case ParticipantEdit:
// LtsDialog::tryLockTopicNew is called by ParticipantEdit::onPartyChanged
// This slot is executed while
// - initialization (signal not connected)
// and
// - by user interaction (signal connected)
bool bIsConnected = isSignalConnected(QMetaMethod::fromSignal(&LtsDialog::signalLockTopic));
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " signalLockTopic(): "
<< ", TopicName =" << m_topicInfo.topicName()
<< ", entity =" << m_topicInfo.entityId()
<< ", signalLockTopic isConnected=" << bIsConnected;
if (bIsConnected)
{
emit signalLockTopic(m_topicInfo); // blocks!
// TODO: setUnchangeable(true);
m_topicInfo.setLocked();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LtsDialog::initTopicLockHandler(const QString &topicName, int entityId)
{
//qWarning() << "initTopicLockHandler";
m_topicInfo.setTopicName(topicName);
m_topicInfo.setEntity(entityId);
m_topicInfo.setValid();
}
void LtsDialog::tryLockTopicNew()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
tryLockTopic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LtsDialog::tryLockTopicNew(const QString &topicName, int entityId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< ", topicName='" << topicName << "'"
<< ", entityId=" << entityId;
// similar to "initTopicLockHandler"
if (m_topicInfo.entityId() != entityId)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " topicName='" << topicName << "'"
<< " change from " << m_topicInfo.entityId() << " to " << entityId;
m_topicInfo.setLocked(false);
}
m_topicInfo.setTopicName(topicName);
m_topicInfo.setEntity(entityId);
m_topicInfo.setValid();
tryLockTopic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< ", topicName='" << topicName << "'"
<< ", entityId=" << entityId;
}
void LtsDialog::triggerTopicLock()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " !!trigger!!";
tryLockTopic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LtsDialog::translate(const QString &title) // Only use if Mike says it's okay!
{
bool isAlreadyUnchangeable = unchangeable();
if (!isAlreadyUnchangeable)
setUnchangeable(true); // retranslation is no change!
setWindowTitle(title);
// qCritical() << " m_topicLockedBy : " << m_topicLockedBy.lockHint
// << ", operatorId=" << m_topicLockedBy.operatorId
// << ", workstationId=" << m_topicLockedBy.workstationId;
if (!isAlreadyUnchangeable)
setUnchangeable(false); // retranslation done
{
bool isNowAlreadyUnchangeable = unchangeable();
QPushButton *b = m_buttonBox->getButton(DialogButton::ReadOnly);
if (b)
{
connect(b, &QPushButton::clicked, this, &LtsDialog::onReadOnlyButtonClicked, Qt::UniqueConnection);
// ExerciseDialog (CloseOnlyDialog -> no Ok-, Apply-Btn)
b->setVisible(isNowAlreadyUnchangeable && !m_topicLockedBy.isEmpty());
if (b->isVisible())
b->setToolTip(tr("Locked by: %1").arg(m_topicLockedBy));
else
b->setToolTip({});
}
}
}
void LtsDialog::slotLock(const QString &lockedBy)
{
setUnchangeable(true);
m_topicLockedBy = lockedBy;
lock();
retranslate();
}
void LtsDialog::slotUnlock()
{
setUnchangeable(false);
m_topicLockedBy.clear();
unlock();
retranslate();
}
void LtsDialog::lock()
{
setWidgetsReadonly<QLineEdit>(true);
setWidgetsReadonly<QDateTimeEdit>(true);
// setWidgetsCustom<QComboBox>([](QComboBox *cb, bool readonly) { if (cb->lineEdit()) cb->lineEdit()->setReadOnly(readonly); }, unchangeable);
setWidgetsReadonly<QSpinBox>(true);
setWidgetsReadonly<QDoubleSpinBox>(true);
setWidgetsReadonly<QPlainTextEdit>(true);
setWidgetsReadonly<QTextEdit>(true);
setWidgetsDisabled<QComboBox>(true);
setWidgetsDisabled<QCheckBox>(true);
setWidgetsDisabled<QRadioButton>(true);
setWidgetsDisabled<QSlider>(true);
setWidgetsDisabled<QTableView>(true);
setWidgetsDisabled<QTableWidget>(true);
setWidgetsDisabled<QListView>(true);
setWidgetsDisabled<QListWidget>(true);
setWidgetsDisabled<QPushButton>(true);
}
void LtsDialog::unlock()
{
restoreReadonly<QLineEdit>();
restoreReadonly<QDateTimeEdit>();
// setWidgetsCustom<QComboBox>([](QComboBox *cb, bool readonly) { if (cb->lineEdit()) cb->lineEdit()->setReadOnly(readonly); }, unchangeable);
restoreReadonly<QSpinBox>();
restoreReadonly<QDoubleSpinBox>();
restoreReadonly<QPlainTextEdit>();
restoreReadonly<QTextEdit>();
restoreEnabled<QComboBox>();
restoreEnabled<QCheckBox>();
restoreEnabled<QRadioButton>();
restoreEnabled<QSlider>();
restoreEnabled<QTableView>();
restoreEnabled<QTableWidget>();
restoreEnabled<QListView>();
restoreEnabled<QListWidget>();
}
void LtsDialog::slotChangesMade() // TODO: get receiver's name, add to changes-made-list to identify changes to specific widgets
{
if (m_unchangeableDialog)
return;
m_changesMade = true;
updateWindowTitle();
m_buttonBox->getButton(DialogButton::Ok)->setEnabled(true);
m_buttonBox->getButton(DialogButton::Apply)->setEnabled(true);
emit signalChangesMade(); // tell dialogs that a Widget has received changes
}
void LtsDialog::resetChanges()
{
m_changesMade = false;
updateWindowTitle();
//if (!m_title.isEmpty())
//setWindowTitle(m_title); // set title back to original - no more [*] needed
m_buttonBox->getButton(DialogButton::Ok)->setEnabled(false);
m_buttonBox->getButton(DialogButton::Apply)->setEnabled(false);
emit signalChangesReset();
}
bool LtsDialog::_apply()
{
if (m_uiRequirements->checkRequirements(this) && apply())
{
resetChanges(); // as all changes were saved successfully
return true;
}
return false;
}
Button LtsDialog::askApplyDiscardCancel()
{
return LtsMessageDialog::askApplyDiscardCancel(this);
}
void LtsDialog::slotApply() // does not close! only saves.
{
_apply();
}
void LtsDialog::slotOkay() // closes and saves. (calls 'apply()', then calls 'close()')
{
if (_apply()) // if there were errors while applying, do not close! If not, close.
_close();
}
void LtsDialog::slotHide()
{
_hide();
}
void LtsDialog::slotClose() // closes but does NOT save (but asks for it (SaveDialog) - if 'save' was selected by the user in the SaveDialog, then 'okay()' will be called).
{
// m_changesMade == true means that apply-btn is enabled
if ((m_changesMade) && (!m_unchangeableDialog))
{
switch (askApplyDiscardCancel())
{
case Button::Apply:
slotOkay(); // save & close
break;
case Button::Discard:
_discard(); // do not save, just close
break;
default: // cancel
break; // do nothing
}
}
else
{
_close(); // nothing has changed, so just close
}
}
void LtsDialog::onReadOnlyButtonClicked()
{
if (m_topicLockedBy.isEmpty())
return;
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Locked by: %1").arg(m_topicLockedBy));
}
void LtsDialog::closeRequest()
{
slotClose();
}
void LtsDialog::_hide()
{
if (m_dialogHelper != nullptr)
{
emit signalHide(m_dialogHelper);
//m_dialogHelper->hide();
}
}
void LtsDialog::_close()
{
resetChanges(); // for safety reasons
aboutToClose();
if (m_dialogHelper != nullptr)
m_dialogHelper->close();
}
void LtsDialog::_discard()
{
dialogCanceled();
_close();
}
} // namespace LtsWidgets
#include "lts_dialog.moc"
+541
View File
@@ -0,0 +1,541 @@
/**
* @RDE_HEADER@
* Dialog for the new Layout system
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_base.h"
#include "lts_widgets_export.h"
#include "ui_requirement.h"
#include "lts_dialog_button_box.h" // so that LtsDialogs don't have to include it
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QPushButton>
#include <QLineEdit>
#include <QComboBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QDateTimeEdit>
#include <QSlider>
#include <QPlainTextEdit>
#include <QTextEdit>
#include <QWidget>
#include <QRadioButton>
#include <QPointer>
#include <QDebug>
namespace LtsWidgets
{
class TopicInfo
{
public:
int entityId() const { return m_entityId; }
const QString &topicName() const { return m_topicName; }
bool isLocked() const { return m_locked; }
bool isValid() const { return m_valid; }
void setEntity(int entityId) { m_entityId = entityId; }
void setTopicName(const QString &topicName) { m_topicName = topicName; }
void setLocked(bool locked = true) { m_locked = locked; }
void setValid() { m_valid = true; }
private:
QString m_topicName;
int m_entityId{0};
bool m_locked{false};
bool m_valid{false};
};
class LtsBaseLayoutWidget;
class LtsDialogHelper;
class LtsProgressDialogHelper;
class LtsDialogButtonBox;
class LtsToolBarArea;
class OverlayContainer;
enum class ButtonCombination
{
OkApplyCancel, // Ok saves changes, closes the Dialog - Apply only saves changes, does not close the Dialog - Cancel closes the Dialog if there are no changes made
OkCancel, // TODO: rm
/* [deprecated => do not use! use 'OkApplyCancel' instead and tell the Dialog that 'this is a new element' by giving the
* translate-method inside the retranslate-method inside your LtsDialog either an empty/new dds element or by setting
* 'isNew' to true on one of the overloaded translate-methods
* OLD: Special case: only use if you are 100% sure what this is => example: New Message: there is only a SEND button, if sent, the dialog MUST be closed
*/
CloseOnly, // For Dialogs that do not save anything
SeamlessDialog, // No buttons, no margins!
};
/**
* @brief The LtsDialog class provides a Dialog for the Lts Layout System and is the equivalent to the QDialog. An LtsDialog can be added to a LayoutArea to interact with the user.
* WARNING: do not add widgets once the window is shown, as the ui requirements cannot be triggered after this point.
*/
class LTSWIDGETS_EXPORT LtsDialog : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool unchangeable READ unchangeable WRITE setUnchangeable)
public:
static int s_object_count;
/**
* @brief LtsDialog TODO: !
* @param parent
*/
explicit LtsDialog(QWidget *parent = nullptr, ButtonCombination buttonCombination = ButtonCombination::CloseOnly, bool closeable = true);
~LtsDialog();
/**
* @brief getHelper returns the internal helper class which contains this LtsDialog on it (do not use, for LtsTabWidget's addTab only!)
* @return class internal helper class
*/
LtsDialogHelper *getHelper();
bool hasUnsavedChanges() const { return m_changesMade || m_currentlyBlocking; }
/**
* @brief retranslate is called on first show and when Qt is told to retranslate the whole application, should be used in all LtsDialogs
* NOTE: Everything inside this method does NOT trigger slotChangesMade (which is good ;). Is called by the framework.
* NOTE: Do not call retranslate yourself. Not even if you've overriden it. That won't work. The frameworks needs to call it.
*/
virtual void retranslate() = 0;
void closeRequest(); // call this from outside of this class to request the close of this dialog (save request dialog is shown if there are unsaved changes)
void setUnchangeable(bool unchangeable = true);
bool unchangeable() const { return m_unchangeableDialog; }
/**
* @brief addDialog
* @param dialog
* @param menuGroup use m_menuGroup / menuGroup() << This is NOT the Tab Title - override 'retranslate' for Tab Titles
* @param identifier use QStringLiteral - example: "exmEditDialog" << This is NOT the Tab Title - override 'retranslate' for Tab Titles
* @param layoutArea
* @return false if dialog / tab already exists - determined by menuGroup + identifier - and so the Dialog / Tab is not added
*/
bool addDialog(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea = LayoutArea::eDialogTab, bool setClosable = true, bool focusNewTab = true);
Q_DECL_DEPRECATED bool addDialogFixed(LtsDialog *dialog, const char *menuGroupUntranslated, const QString &identifier, const LayoutArea &layoutArea = LayoutArea::eDialogTab, bool setClosable = true, bool focusNewTab = true);
bool focusDialog(const QString &menuGroup, const QString &identifier, const LayoutArea &layoutArea);
bool amIFocused() const;
void setBaseLayoutWidget(LtsBaseLayoutWidget *baseLayoutWidget); // Do not use.
void __unchange__() { resetChanges(); } // Do not use.
void setClosable(bool closable) { m_isClosable = closable; }
bool isClosable() const { return m_isClosable; }
virtual void aboutToClose() {} // called just before the dialog is closed
void setMenuGroup(const QString &menuGroup) { m_menuGroup = menuGroup; } // Do not call. Called by Framework.
const QString &menuGroup() const { return m_menuGroup; }
void setIdentifier(const QString &identifier); // Do not call. Called by Framework.
virtual void tableColumnOrganiserSetting(){};
const QString &identifier() const { return m_identifier; }
void setOverlay(QWidget *widget);
void unsetOverlay();
/**
* @brief Sets the current width of the given widget as minimum width.
* Useful for Labels showing changing values to not jitter when then value changes
* Therefore you have to make sure, that the biggest (textual) value is set when calling this function
* @param Widget to set fixed width for
*/
static inline void setMinWidthForWidgetHelper(QWidget *w)
{
int width = w->sizeHint().width();
w->setMinimumWidth(width);
}
QString windowTitle() const
{
return m_title;
}
// neu tryLockTopic methode mit n Parametern, je nach Einsatzfall:
// ( LtsWidgets::TopicInfo )
// Diese n Überladungen entsprechen jeweils eine Lock-Bedarfsfall.
// a) Dlg über pro Exercise sperren ("Exercise Purpose")
// b) Dlg über pro Exercise, PartyId sperren ("Edit Partitipants")
// jeder PartyChanged @ "Edit Partitipants" aktualisieren
// c) 2 verschiedene Dialog (pro Exercise) -> nur einer ReadWrite
// In beiden Dialog DENSELBEN topicName nutzen
// "Gesperrt von anzeigen"
// a)
void tryLockTopicNew();
// b)
virtual void tryLockTopicNew (const QString &topicName, int entityId = -1);
void triggerTopicLock();
void setOnlineHelpFilename(const QString &fileName); // string without file extension !!
void setCurrentlyBlocking(bool currentlyBlocking = true) { m_currentlyBlocking = currentlyBlocking; }
signals:
/**
* @brief Emit this signal to start the onHideAnimated Animation
* Only works when the dialog is a child of the LtsBaseLayoutWidget
* @param layoutArea the LayoutArea that should hide with an animation
*/
void startMainSelect(LtsWidgets::LayoutArea);
/**
* @brief Emit this signal to start the onReShowAnimated Animation
* Only works when the dialog is a child of the LtsBaseLayoutWidget
* @param layoutArea the LayoutArea that should be shown again with an animation
*/
void endMainSelect(LtsWidgets::LayoutArea);
/**
* @brief signalChangesMade is emitted after slotChangesMade was called
* (if you want to enable the Ok and/or Apply button, use slotChangesMade)
* For a list of supported Widgets, see LtsDialog::showEvent.
*/
void signalChangesMade();
void signalHide(QObject *object); // tells the TabWidget to remove the tab
/**
* @brief Emit tells that all changes are reset
* and ok/apply buttons are now disabled
*/
void signalChangesReset(); //
void signalFocused();
void visibilityChanged(bool visible);
void signalLockTopic(const LtsWidgets::TopicInfo &topicInfo);
void signalShowOnlineHelp(const QString &fileName);
protected:
/**
* @brief translate #1 is the same as translate #2, but does not retranslate the ui. DO NOT USE until permission by Mike!
* @param title
*/
void translate(const QString &title);
template <typename T>
/**
* @brief translate #2 is the same as translate #3, but only sets the title by a single parameter. DO NOT USE until permission by Mike!
* @param ui
* @param title
*/
void translate(T *ui, const QString &title) // main translate method! Only use if Mike says it's okay!
{
translate(title);
if (ui)
ui->retranslateUi(this);
// after ui->retranslate() to make sure all labels have the correct content
m_uiRequirements->retranslate();
}
void hideEvent(QHideEvent *event) override
{
QWidget::hideEvent(event);
emit visibilityChanged(false);
}
template <typename T>
/**
* @brief translate #3 is the same as translate #4, but lacks Topic handling. The window title is a combination of the given parameters.
* @param ui
* @param custom
* @param isNew set to true if this dialog is about creating a new entry (equivalent to Topic id 0)
* @param title
*/
void translate(T *ui, const QString &custom, bool isNew, const QString &title)
{
QString newTitle;
if (!isNew)
{
if (unchangeable())
{
if (custom.isEmpty())
newTitle = (tr("Show %1").arg(title));
else
newTitle = (tr("Show %1: %2").arg(title, custom));
}
else
{
if (custom.isEmpty())
newTitle = (tr("Edit %1").arg(title));
else
newTitle = (tr("Edit %1: %2").arg(title, custom));
// tryLockTopic();
tryLockTopicNew();
}
}
else
{
newTitle = (tr("New %1").arg(title));
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->hide(); // otherwise applying something after clicking apply results in another new object being created
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setText(tr("Create")); // Deutsch: "Anlegen"
}
translate(ui, newTitle);
}
template <typename T>
/**
* @brief translate #4 is the same as translate #5, but without a Topic. 'customUniqueTopicName' and 'topicId' replace the missing Topic.
* @param ui
* @param customUniqueTopicName the name you would give the Topic class (e.g. "MeteoDataManagement::StaticWeather")
* @param topicId the Topic's id (e.g. 1) - use id smaller than 1 if it's a non-existent (new) entry
* @param custom
* @param title
*/
void translate(T* ui, const QString &customUniqueTopicName, int topicId, const QString &custom, const QString &title)
{
Q_UNUSED(customUniqueTopicName);
initTopicLockHandler(customUniqueTopicName, topicId);
translate(ui, custom /* can be empty */,
(topicId < 1 /* 0 is dds's equivalent to 'new', sadly there are cases where the id == -1, catch this case */),
title);
}
template <typename T, typename S>
/**
* @brief translate #5 is the same as translate #6, but uses 'custom' as Topic name (for Topics without a 'name' entry)
* @param ui
* @param data
* @param custom the Topic's name (e.g. "Test Exercise 42")
* @param title
*/
void translate(T *ui, const S &data, const QString &custom, const QString &title) // same as 'void translate(T *ui, const S &data, const QString &title)', but for topics without the 'name' entry
{
translate(ui, QString::fromUtf8(typeid(data).name()), data.id(), custom /* can be empty */, title);
}
template <typename T, typename S>
/**
* @brief translate #6 should be the most common and is easiest translate method for exactly one Topic.
* It locks the specific topic and creates a complex tab title with only a Topic and a general title.
* @param ui
* @param data the Topic which contains the id and the name (e.g. instance of ExerciseManagement::Exercise)
* @param title the topic of this dialog (e.g. "Exercise" if this Dialog is about a specific Exercise)
*/
void translate(T *ui, const S &data, const QString &title)
{
translate(ui, data, QString::fromStdString(data.name()), title); // -> translate #5
}
// this is called when the dialog was cancel to be able to reset changes made
virtual void dialogCanceled() {}
LtsBaseLayoutWidget* baseLayoutWidget();
void setNoTopicLock() { m_noLockTopic = true; } // Call in LtsDialog c_tor and nowhere else!
private:
friend class LtsDialogHelper;
bool m_changesMade;
bool m_currentlyBlocking { false }; // set to true if the dialog must not be closed because it is currently in a blocking call
bool m_firstShow;
QPointer<LtsBaseLayoutWidget> m_baseLayoutWidget; // "cannot" outlive BLW, but to be sure..
bool m_isClosable;
TopicInfo m_topicInfo;
public:
QString m_topicLockedBy;
private:
bool m_noLockTopic;
bool m_unchangeableDialog; // no '[*]' or asking in case of unsaved changes
QPointer<LtsDialogHelper> m_dialogHelper;
//LtsProgressDialogHelper *m_progressDialogHelper;
QString m_title;
QString m_onlineHelpFileName;
QString m_menuGroup;
QString m_identifier;
// overlay stuff:
QPointer<OverlayContainer> m_overlayContainer;
QPointer<QWidget> m_overlayWidget;
bool _apply();
void _close();
void _discard();
void _hide();
void resetChanges();
void addDebugMenuButton();
void tryLockTopic();
void initTopicLockHandler(const QString &topicName, int entityId);
/**
* @brief setWindowTitle sets this window title and of the Dialog Helper (use retranslate() instead!)
* @param title
*/
void setWindowTitle(const QString &title);
void updateWindowTitle();
void addConnections(QWidget *widget);
template <typename WidgetType, typename SignalFunc>
bool addChangesMadeSignal(QWidget *widget, SignalFunc signal)
{
WidgetType *concreteWidget = qobject_cast<WidgetType *>(widget);
if (!concreteWidget)
return false;
connect(concreteWidget, signal, this, &LtsDialog::slotChangesMade);
return true;
}
template <typename WidgetType>
void setWidgetsReadonly(bool readonly)
{
const auto children = findChildren<WidgetType *>();
for (WidgetType *widget : children )
{
widget->setProperty("lastReadOnlyState", widget->isReadOnly());
widget->setReadOnly(readonly);
}
}
template <typename WidgetType>
void setWidgetsDisabled(bool disabled)
{
const auto children = findChildren<WidgetType *>();
for (WidgetType *widget : children)
{
widget->setProperty("lastEnabledState", widget->isEnabled());
widget->setEnabled(!disabled);
}
}
template <typename WidgetType>
void restoreReadonly()
{
const auto children = findChildren<WidgetType *>();
for (WidgetType *widget : children)
{
QVariant lastState = widget->property("lastReadOnlyState");
if (lastState.isValid() && !lastState.isNull() && lastState.canConvert(QMetaType::Bool))
widget->setReadOnly(lastState.toBool());
}
}
template <typename WidgetType>
void restoreEnabled()
{
const auto children = findChildren<WidgetType *>();
for (WidgetType *widget : children)
{
QVariant lastState = widget->property("lastEnabledState");
if (lastState.isValid() && !lastState.isNull() && lastState.canConvert(QMetaType::Bool))
widget->setEnabled(lastState.toBool());
}
}
public slots:
/**
* @brief slotChangesMade emits 'signalChangesMade()' and tells the dialog that changes on editable Widgets were made. So that the Save-Dialog will be shown on Close.
* For a list of supported Widgets, see LtsDialog::showEvent.
*/
void slotChangesMade();
void slotHide(); // hides dialog, removes tab, does not delete dialog
void slotLock(const QString &lockedBy);
void slotUnlock();
private slots:
void slotApply(); // does not close! only saves.
void slotOkay(); // closes and saves. (calls 'apply()', then calls 'close()')
void slotClose(); // closes but does NOT save (but asks for it (SaveDialog) - if 'save' was selected by the user in the SaveDialog, then 'okay()' will be called).
//void slotSizeChanged(const QSize &size);
void onReadOnlyButtonClicked();
protected:
virtual void lock();
virtual void unlock();
virtual bool apply() { return true; } // reimpl to save your changes here, DO NOT CLOSE! return true if changes could be saved (this will close the dialog on 'okay') or return false if NOT (dialog will NOT be closed then)
void showEvent(QShowEvent *event) override;
Button askApplyDiscardCancel();
LtsDialogButtonBox *m_buttonBox;
UiRequirementList *m_uiRequirements;
NGL::INGLLogger &m_logger;
};
// support init WITHOUT parameter
template<typename T>
T *initDialog(T *dialog)
{
if (!dialog->init())
{
dialog->deleteLater();
return nullptr;
}
return dialog;
}
// support init WITH parameter
template<typename T, typename ... F>
T *initDialog(T *dialog, F&&... f)
{
if (!dialog->init(std::forward<F>(f)...))
{
dialog->deleteLater();
return nullptr;
}
return dialog;
}
template<typename T, typename ... F>
T *createThrowableDialog(F&&... f)
{
T *dlg = nullptr;
try
{
dlg = new T(std::forward<F>(f)...);
}
catch (const char *s)
{
fprintf(stderr, "Dialog could not be created: %s\n", s);
return nullptr;
}
catch (const QString &msg)
{
fprintf(stderr, "Dialog could not be created: %s\n", msg.toStdString().c_str());
return nullptr;
}
catch (...)
{
fprintf(stderr, "Dialog could not be created: unknown error\n");
return nullptr;
}
return dlg;
}
} // namespace LtsWidgets
+224
View File
@@ -0,0 +1,224 @@
/**
* @RDE_HEADER@
* Bottom Button Box for Dialogs which need actions
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-08-17
*/
#include "lts_dialog_button_box.h"
#include "ui_lts_dialog_button_box.h"
#include "lts_message_dialog.h"
#include <LtsSession/lts_session_data.h>
#include <QMenu>
namespace LtsWidgets
{
LtsDialogButtonBox::LtsDialogButtonBox(QWidget *parent)
: QWidget(parent)
, m_isTablet(LtsSessionData::LtsSessionData::getInstance()->isTablet())
, m_ui(new Ui::LtsDialogButtonBox)
{
m_ui->setupUi(this);
_setVisibilityForAll(false); // hide all buttons
// Btn 'Library'
QMenu *libraryMenu = new QMenu(m_ui->pbLibrary);
libraryMenu->addAction(QIcon(QLatin1String(":/style/library_load.svg")),
tr("Load"), this, &LtsDialogButtonBox::signalLibraryLoadClicked);
libraryMenu->addAction(QIcon(QLatin1String(":/style/library_save.svg")),
tr("Save"), this, &LtsDialogButtonBox::signalLibrarySaveClicked);
m_ui->pbLibrary->setMenu(libraryMenu);
// exceptions for call above:
m_ui->pbOk->setVisible(true); // c LtsDialog's impl 2 c y !
m_ui->pbClose->setVisible(true); // s.a.a.
// no help file name set = no help button ! => m_ui->pbHelp->setVisible(true); // must be explicitly hidden for dialogs without help content
}
LtsDialogButtonBox::~LtsDialogButtonBox()
{
delete m_ui;
}
void LtsDialogButtonBox::_setVisibilityForAll(bool visible)
{
const auto buttons = findChildren<QPushButton *>();
for (QPushButton *button : buttons)
button->setVisible(visible);
}
void LtsDialogButtonBox::setText(DialogButton button, const QString &text)
{
// TODO: check if setText was called by a 'retranslate' method, if not debug msg !
QPushButton *btn = getButton(button);
if (btn)
btn->setText(text);
}
void LtsDialogButtonBox::setToolTip(DialogButton button, const QString &text)
{
QPushButton *btn = getButton(button);
if (btn)
btn->setToolTip(text);
}
void LtsDialogButtonBox::setIcon(DialogButton button, const QIcon &icon)
{
QPushButton *btn = getButton(button);
if (btn)
btn->setIcon(icon);
}
void LtsDialogButtonBox::setMenu(DialogButton button, QMenu *menu)
{
QPushButton *btn = getButton(button);
if (btn)
btn->setMenu(menu);
}
QPushButton *LtsDialogButtonBox::getButton(DialogButton button) const
{
switch (button)
{
case DialogButton::ReadOnly:
return m_ui->pbReadOnly;
case DialogButton::Ok:
return m_ui->pbOk;
case DialogButton::Apply:
return m_ui->pbApply;
case DialogButton::Close:
return m_ui->pbClose;
case DialogButton::Yes:
return m_ui->pbYes;
case DialogButton::No:
return m_ui->pbNo;
case DialogButton::Remove:
return m_ui->pbRemove;
case DialogButton::Hide:
return m_ui->pbHide;
case DialogButton::Add:
return m_ui->pbAdd;
case DialogButton::Help:
return m_ui->pbHelp;
case DialogButton::Edit:
return m_ui->pbEdit;
case DialogButton::Save:
return m_ui->pbSave;
case DialogButton::Load:
return m_ui->pbLoad;
case DialogButton::Show:
return m_ui->pbShow;
case DialogButton::Import:
return m_ui->pbImport;
case DialogButton::Export:
return m_ui->pbExport;
case DialogButton::Library:
return m_ui->pbLibrary;
case DialogButton::CenterMap:
return m_ui->pbCenterMap;
case DialogButton::ZoomMapTo:
return m_ui->pbZoomMapTo;
case DialogButton::Print:
return m_ui->pbPrint;
#ifdef LTS_DEBUG
case DialogButton::_debug_:
return m_ui->_pbChanges;
#endif
}
Q_ASSERT(false);
return nullptr; // sec
}
void LtsDialogButtonBox::slotRemoveClicked()
{
if (LtsMessageDialog::showMessage(this,
tr("Are you sure you want to delete the selected entry(ies)?"),
tr("Remove selected entry(ies)"),
Behaviour::Modal,
Button::Yes | Button::No)
== Button::Yes)
emit signalRemoveClicked();
}
void LtsDialogButtonBox::setButtonsVisible(QPushButton *pb)
{
if (!pb || (pb == m_ui->pbPrint && m_isTablet))
return;
m_ui->line->setVisible(true);
pb->setVisible(true);
}
void LtsDialogButtonBox::setButtonVisible(DialogButton button, bool visible)
{
QPushButton *pb = getButton(button);
if (!pb)
return;
if (m_isTablet && pb == m_ui->pbPrint)
{
pb->setVisible(false);
return;
}
pb->setVisible(visible);
}
bool LtsDialogButtonBox::isButtonVisible(DialogButton button) const
{
const QPushButton *pb = getButton(button);
return (pb ? pb->isVisible() : false);
}
void LtsDialogButtonBox::setButtonEnabled(DialogButton button, bool enabled)
{
QPushButton *pb = getButton(button);
switch (button)
{
case DialogButton::Close:
case DialogButton::Ok:
case DialogButton::Apply:
//case DialogButton::Yes:
//case DialogButton::No:
{
#ifdef LTS_DEBUG
LtsMessageDialog::showMessage(this, QStringLiteral("Hier stimmt etwas nicht. Bitte MIKE bescheid geben WAS geklickt oder gemacht wurde als dieser Dialog auftauchte, damit ich es fixen kann. Danke."), QStringLiteral(">>> STOP <<<"));
#endif
break;
}
default:
break;
}
if (pb)
pb->setEnabled(enabled);
}
bool LtsDialogButtonBox::isButtonEnabled(DialogButton button) const
{
auto *that = const_cast<LtsDialogButtonBox *>(this); // ...
const QPushButton *pb = that->getButton(button);
return (pb ? pb->isEnabled() : false);
}
} // namespace LtsWidgets
+252
View File
@@ -0,0 +1,252 @@
/**
* @RDE_HEADER@
* Bottom Button Box for Dialogs which need actions
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-08-17
*/
#pragma once
#include "lts_widgets_export.h"
#include "lts_message_dialog.h"
#include <QWidget>
#include <QPushButton>
namespace Ui
{
class LtsDialogButtonBox;
}
namespace LtsWidgets
{
enum class DialogButton
{
// Do not connect these:
ReadOnly,
Ok,
Apply,
Close,
Yes,
No,
// You can connect these:
Remove,
Hide,
Add,
Help,
Edit,
Save,
Load,
Show,
Import,
Export,
CenterMap,
ZoomMapTo,
Library,
Print,
#ifdef LTS_DEBUG
_debug_,
#endif
};
// TODO: doc: close is now autoconnected by NEW LtsDialogs not running in compatibility mode !
/**
* @brief The LtsDialogButtonBox class is a replacement for the NGLDialogButtonBox. With the significant difference that
* Actions ('Ok', 'Close', etc.) are select in code, not in the designer.
*
* Pro:
* > no dead/unconnected buttons
* > no designer plugin needed
* > no killed dialog boxes after editing uis
* > auto connecting the close button to the Dialogs accept / Widgets close (important for close requests on dialogs before closing)
* > solid connections only (safer & faster than connections by char*) [also con]
*
* Con:
* > no possibility to see which Actions are available for a specific dialog
* > solid connections only (more complicated) [also pro]
*
* To make Actions visible & usable, use 'connectButton' with the coherant Action (DialogButton).
* 'connectButton' works similar to Qt's Connect mechanism, with the main difference that there is neither a sender nor a signal to specify.
*
* Example: connectButton(DialogButton::Ok, this, &ThisDialog::accept);
*
*
* >>You have overloaded slots?<<
* For example you have: slotSomethingChanged(int) and slotSomethingChanged(QString)
* For the 'int'-version the result should look like: connect(DialogButton::Ok, this, static_cast<void (ThisDialog::*)(int)>(&ThisDialog::slotSOmethingChanged));
* Unfortunately this is the Qt version to solve this problem..
*
*/
class LTSWIDGETS_EXPORT LtsDialogButtonBox : public QWidget
{
Q_OBJECT
public:
explicit LtsDialogButtonBox(QWidget *parent);
~LtsDialogButtonBox();
/**
* @brief connectButton connects a Button (like 'Apply', 'Ok', etc.) to the given receiver + slot.
* This method prevents the existence of dead buttons (not connected) and that a missing Designer plugin messes up the button properties.
* So, to make a button visible, use this method.
*
* Important: only solid connections possible (yet)
* Example: connectButton(DialogButton::Ok, this, &ThisDialog::accept);
*
* @param button the DialogButton to make visible and connect it to the given 'slot'
* @param receiver the receiver of the signal when a DialogButton is clicked - like the receiver in Qt's connect method
* @param slot the slot on the 'receiver' called when the clicked signal is called by a DialogButton - like the slot in the Qt's connect method
* @param type Qt::ConnectionType (does not need bo be set)
*/
template <typename Func2>
void connectButton(DialogButton button, const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
Func2 slot, Qt::ConnectionType type = Qt::AutoConnection)
{
//if (!LtsDialog::plan_editor_compatibility)
{
switch (button)
{
#ifdef LTS_DEBUG
case DialogButton::_debug_:
#endif
case DialogButton::Close:
case DialogButton::Ok:
case DialogButton::ReadOnly:
case DialogButton::Apply:
case DialogButton::Help:
{
LtsMessageDialog::showMessage(this,
QStringLiteral("Bitte den fuer das Plugin verantwortlichen kontaktieren:<br />"
"m_buttonBox nicht mit Ok, Apply, Help, _debug_ oder Close verbinden<br />"
"Note: Bitte nur m_buttonBox verwenden, keine LtsDialogButtonBox selber erstellen"
"Ok & Apply werden automatisch zur virtual Methode 'apply()' im LtsDialog weitergeleitet.<br />"
"Close schließt einfach den Dialog - im Destruktor oder in 'aboutToClose()' ist der 'Close Code' gut aufgehoben.<br />"
"<br />"
"FIXEN !!!!!"),
QStringLiteral("--- STOP --- !!! FIXEN !!! ---"));
break;
}
default:
break;
}
}
QPushButton *theSender = getButton(button);
if (!theSender)
return;
// special cases for Buttons with embedded Dialogs
switch (button)
{
case DialogButton::Remove:
{
connect(this, &LtsDialogButtonBox::signalRemoveClicked, receiver, slot, type);
connect(theSender, &QPushButton::clicked, this, &LtsDialogButtonBox::slotRemoveClicked);
break;
}
case DialogButton::Export:
case DialogButton::Import:
{
if (m_isTablet)
{
theSender->setVisible(false);
return;
}
Q_FALLTHROUGH();
}
default:
connect(theSender, &QPushButton::clicked, receiver, slot, type);
break;
}
setButtonsVisible(theSender); // this method's magic
}
template <typename Func1, typename Func2>
bool connectLibraryButton(const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
Func1 slotLoad,
Func2 slotSave,
Qt::ConnectionType type = Qt::AutoConnection)
{
QPushButton *theSender = getButton(DialogButton::Library);
if (!theSender)
return false;
if (m_isTablet)
{
theSender->setVisible(false);
return true;
}
setButtonsVisible(theSender);
type = static_cast<Qt::ConnectionType>(type | Qt::UniqueConnection);
return (connect(this, &LtsDialogButtonBox::signalLibraryLoadClicked, receiver, slotLoad, type) &&
connect(this, &LtsDialogButtonBox::signalLibrarySaveClicked, receiver, slotSave, type));
}
/**
* @brief setText changes the text of a DialogButton to the 'text' specified.
* @param button the DialogButton to rename
* @param text new text of DialogButton
*/
void setText(DialogButton button, const QString &text);
void setToolTip(DialogButton button, const QString &text);
void setIcon(DialogButton button, const QIcon &icon);
void setMenu(DialogButton button, QMenu *menu);
/**
* @brief setButtonEnabled enables / disables a DialogButton.
* @param button the DialogButton to dis-/enable
* @param enabled true = enabled; false = disabled
*/
void setButtonEnabled(DialogButton button, bool enabled = true);
bool isButtonEnabled(DialogButton button) const;
void setButtonVisible(DialogButton button, bool visible = true);
bool isButtonVisible(DialogButton button) const;
/**
* Do not use!
* @brief getButton DialogButton to real QPushButton* widget (do not use outside of this class!)
* @param button
* @return Pointer to QPushButton or nullptr when not found / available
*/
QPushButton *getButton(DialogButton button) const;
/**
* @brief _showAll Do not use.
*/
void _setVisibilityForAll(bool visible);
signals:
void signalRemoveClicked();
void signalHelpClicked();
void signalLibraryLoadClicked();
void signalLibrarySaveClicked();
private slots:
void slotRemoveClicked();
private:
const bool m_isTablet;
/**
* @brief setButtonVisible makes a QPushButton visible and also shows the horizontal line, etc. when it's first called (every button box is empty before one button was made visible)
* @param pb
*/
void setButtonsVisible(QPushButton *pb);
Ui::LtsDialogButtonBox *m_ui;
};
} // namespace LtsWidgets
+326
View File
@@ -0,0 +1,326 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsDialogButtonBox</class>
<widget class="QWidget" name="LtsDialogButtonBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1706</width>
<height>46</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="leftSide" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="_pbChanges">
<property name="text">
<string notr="true">[debug]</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbHelp">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/help_index.svg</normaloff>:/style/help_index.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbPrint">
<property name="text">
<string>Print</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/freq_plan.svg</normaloff>:/style/freq_plan.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbLibrary">
<property name="text">
<string>Library</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/library.svg</normaloff>:/style/library.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbImport">
<property name="text">
<string>Import</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/export_documents.svg</normaloff>:/style/export_documents.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbExport">
<property name="text">
<string>Export</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/import_documents.svg</normaloff>:/style/import_documents.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCenterMap">
<property name="text">
<string>Center Map</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/center_to_object.svg</normaloff>:/style/center_to_object.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbZoomMapTo">
<property name="text">
<string>Zoom to</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/zoom_map_to.svg</normaloff>:/style/zoom_map_to.svg</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="rightSide" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pbReadOnly">
<property name="text">
<string>ReadOnly</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/warning.svg</normaloff>:/style/warning.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbOk">
<property name="text">
<string>OK</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/dialog-ok.svg</normaloff>:/style/dialog-ok.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbApply">
<property name="text">
<string>Apply</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/dialog-ok.svg</normaloff>:/style/dialog-ok.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbLoad">
<property name="text">
<string>Load</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/export_documents.svg</normaloff>:/style/export_documents.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbSave">
<property name="text">
<string>Save</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/import_documents.svg</normaloff>:/style/import_documents.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAdd">
<property name="text">
<string>Add</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/document-new.svg</normaloff>:/style/document-new.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbEdit">
<property name="text">
<string>Edit</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/document-edit.svg</normaloff>:/style/document-edit.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbShow">
<property name="text">
<string>Show</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/filter_map_settings.svg</normaloff>:/style/filter_map_settings.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbYes">
<property name="text">
<string>Yes</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/dialog-ok.svg</normaloff>:/style/dialog-ok.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbNo">
<property name="text">
<string>No</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/dialog-close.svg</normaloff>:/style/dialog-close.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbRemove">
<property name="text">
<string>Remove</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/media-vcr-nopause.svg</normaloff>:/style/media-vcr-nopause.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbHide">
<property name="text">
<string>Hide</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/show_building_configuration.svg</normaloff>:/style/show_building_configuration.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbClose">
<property name="text">
<string>Close</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/dialog-close.svg</normaloff>:/style/dialog-close.svg</iconset>
</property>
<property name="shortcut">
<string>Ctrl+W</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+180
View File
@@ -0,0 +1,180 @@
/**
* @RDE_HEADER@
* Helper Class for LtsDialog - creates Dialog Button Box and Scroll Area automatically
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-09-23
*
*/
#include "lts_dialog_helper.h"
#include "lts_dialog.h"
#include "ui_lts_dialog_helper.h"
#include <QScrollArea>
#include <QScrollBar>
#include <QDebug>
namespace LtsWidgets
{
class LDHScrollBar : public QScrollBar
{
Q_OBJECT
public:
using QScrollBar::QScrollBar;
void showEvent(QShowEvent *e) override
{
QScrollBar::showEvent(e);
emit scrollbarVisiblityChanged(true);
}
void hideEvent(QHideEvent *e) override
{
QScrollBar::hideEvent(e);
emit scrollbarVisiblityChanged(false);
}
Q_SIGNALS:
void scrollbarVisiblityChanged(bool visible);
};
LtsDialogHelper::LtsDialogHelper(QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::LtsDialogHelper())
, m_dialog(nullptr)
{
m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
auto hScroll = new LDHScrollBar(this);
auto vScroll = new LDHScrollBar(this);
m_ui->scrollArea->setHorizontalScrollBar(hScroll);
m_ui->scrollArea->setVerticalScrollBar(vScroll);
connect(hScroll, &LDHScrollBar::scrollbarVisiblityChanged,
this, &LtsDialogHelper::onHScrollBarVisibilityChanged);
connect(vScroll, &LDHScrollBar::scrollbarVisiblityChanged,
this, &LtsDialogHelper::onVScrollBarVisibilityChanged);
#ifdef TABLET_MODE
QList<QWidget *> scrollWidgets = findChildren<QWidget *>();
for (auto i = scrollWidgets.begin(); i != scrollWidgets.end(); ++i)
{
QWidget *widget = *i;
QVariant isConst = widget->property("const");
if (isConst.isValid() && isConst.toBool())
continue;
LtsWidgets::addTouchScroll(widget);
}
#endif
m_ui->buttonBox->setMinimumHeight(m_ui->buttonBox->sizeHint().height());
}
LtsDialogHelper::~LtsDialogHelper()
{
m_ui->scrollArea->horizontalScrollBar()->disconnect(this);
m_ui->scrollArea->verticalScrollBar()->disconnect(this);
delete m_ui;
}
LtsDialogButtonBox *LtsDialogHelper::getButtonBox()
{
return m_ui->buttonBox;
}
void LtsDialogHelper::setDialog(LtsDialog *dialog)
{
if (m_dialog != nullptr) // set dialog only one time
return;
m_dialog = dialog;
m_dialog->setContentsMargins(QMargins());
if (m_dialog->layout())
m_dialog->layout()->setContentsMargins(QMargins());
m_ui->scrollArea->setWidget(m_dialog);
//if (LtsDialog::plan_editor_compatibility)
{
connect(dialog, &LtsDialog::destroyed, this, [this]()
{
m_dialog = nullptr;
deleteLater();
});
}
// connect(this, &LtsDialogHelper::signalSizeChanged,
// dialog, &LtsDialog::slotSizeChanged);
}
LtsDialog *LtsDialogHelper::getDialog()
{
return m_dialog;
}
void LtsDialogHelper::retranslate()
{
if (m_dialog == nullptr)
return;
m_dialog->setContentsMargins(QMargins());
if (m_dialog->layout())
m_dialog->layout()->setContentsMargins(QMargins());
bool oldChangesFlag = m_dialog->m_changesMade;
// TODO: clear uiRequirements !! But tell devs first to move all uiReqs to retranslate()
m_dialog->retranslate();
if (oldChangesFlag)
m_dialog->slotChangesMade();
else
m_dialog->resetChanges();
}
void LtsDialogHelper::resizeEvent(QResizeEvent *e)
{
emit signalSizeChanged(e->size());
}
void LtsDialogHelper::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange)
retranslate();
}
void LtsDialogHelper::onHScrollBarVisibilityChanged(bool visible)
{
if (m_dialog == nullptr || !m_dialog->layout())
return;
QMargins m = m_dialog->layout()->contentsMargins();
m.setBottom(visible ? -1 : 0);
m_dialog->layout()->setContentsMargins(m);
}
void LtsDialogHelper::onVScrollBarVisibilityChanged(bool visible)
{
if (m_dialog == nullptr || !m_dialog->layout())
return;
QMargins m = m_dialog->layout()->contentsMargins();
m.setRight(visible ? -1 : 0);
m_dialog->layout()->setContentsMargins(m);
}
}
#include "lts_dialog_helper.moc"
+60
View File
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* Helper Class for LtsDialog - creates Dialog Button Box and Scroll Area automatically
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-09-23
*
*/
#pragma once
#include <QWidget>
#include <QSize>
namespace Ui
{
class LtsDialogHelper;
}
namespace LtsWidgets
{
class LtsDialogButtonBox;
class LtsDialog;
/**
* @brief The LtsDialogHelper class => ***DO NOT USE***, so no export!
*/
class LtsDialogHelper : public QWidget
{
Q_OBJECT
public:
explicit LtsDialogHelper(QWidget *parent = nullptr);
~LtsDialogHelper();
LtsDialogButtonBox *getButtonBox();
void setDialog(LtsDialog *dialog);
LtsDialog *getDialog();
bool compare(const LtsDialog *dialog) const { return (dialog == m_dialog); } // to keep constness...
void retranslate();
signals:
void signalSizeChanged(const QSize &size);
private:
void resizeEvent(QResizeEvent *e) override;
void changeEvent(QEvent *e) override;
void onHScrollBarVisibilityChanged(bool visible);
void onVScrollBarVisibilityChanged(bool visible);
Ui::LtsDialogHelper *m_ui;
LtsDialog *m_dialog;
};
} // namespace LtsWidgets
+70
View File
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsDialogHelper</class>
<widget class="QWidget" name="LtsDialogHelper">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>456</width>
<height>306</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
</widget>
</item>
<item alignment="Qt::AlignBottom">
<widget class="LtsWidgets::LtsDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>150</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDialogButtonBox</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_dialog_button_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+494
View File
@@ -0,0 +1,494 @@
/**
* @RDE_HEADER@
* Draggable Icon with Text for Menus
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_drag_icon.h"
#include <QDrag>
#include <QDragEnterEvent>
#include <QDragLeaveEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QAction>
#include <QVBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <QToolButton>
#include <QButtonGroup>
#include <QPainter>
namespace LtsWidgets
{
class LtsToolButton : public QToolButton
{
Q_OBJECT
public:
explicit LtsToolButton(QWidget *parent)
: QToolButton(parent)
, m_sameWidgetDrop(false)
, m_isDraggable(true)
, m_startPos(QPoint())
{
setAutoFillBackground(false);
setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
}
virtual ~LtsToolButton() = default;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void setSameWidgetDrop(bool drop) { m_sameWidgetDrop = drop; }
bool sameWidgetDrop() const { return m_sameWidgetDrop; }
void setDraggable(bool isDraggable) { m_isDraggable = isDraggable; }
void dropEvent(QDropEvent *event) override
{
emit newDropEvent(event);
QToolButton::dropEvent(event);
}
void addMimeData(const QString &mimeType, const QByteArray &data);
bool removeMimeData(const QString &mimeType);
void removeAllMimeData() { m_mimeData.clear(); }
protected:
void initButton();
signals:
void newDropEvent(QDropEvent *e);
void dropSuc();
private:
void performDrag(QMouseEvent *event);
bool m_sameWidgetDrop;
bool m_isDraggable;
QPoint m_startPos;
QMap<QString, QByteArray> m_mimeData;
};
LtsDragIcon::LtsDragIcon(QWidget *parent)
: QWidget(parent)
, m_lay(nullptr)
, m_toolButton(nullptr)
, m_icon()
, m_dragLabel(new QLabel(this))
, m_text()
, m_style(ButtonStyle::eAll)
, m_forceFixedSize(false)
, m_isDraggable(true)
, m_dropHandlerKey(0L)
, m_buttonSize(ButtonSize::eDefault)
, m_showDragIcon(true)
{
setAutoFillBackground(false);
initButton();
}
void LtsDragIcon::setStyleToButton()
{
if (nullptr == m_toolButton) return;
switch (m_style)
{
case ButtonStyle::eIconOnly:
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
setFixedSize(QSize(static_cast<int>(m_buttonSize), static_cast<int>(m_buttonSize)));
m_toolButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
} break;
case ButtonStyle::eTextOnly:
{
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_toolButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
setFixedSize( QSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX) );
m_toolButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
layout()->setSizeConstraint( QLayout::SetDefaultConstraint );
} break;
case ButtonStyle::eTextNextToIcon:
{
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_toolButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
setFixedSize( QSize(QWIDGETSIZE_MAX,QWIDGETSIZE_MAX) );
m_toolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
layout()->setSizeConstraint( QLayout::SetDefaultConstraint );
} break;
default: break;
}
} // void LtsDragIcon::setStyleToButton()
void LtsDragIcon::initButton()
{
setMinimumSize(static_cast<int>(m_buttonSize), static_cast<int>(m_buttonSize));
delete m_toolButton;
delete m_lay;
delete layout();
m_toolButton = new LtsToolButton(this);
m_toolButton->setDraggable(m_isDraggable);
connect(m_toolButton, &LtsToolButton::clicked, this, &LtsDragIcon::clicked);
connect(m_toolButton, &LtsToolButton::toggled, this, &LtsDragIcon::toggled);
connect(m_toolButton, &LtsToolButton::newDropEvent, this, &LtsDragIcon::newDropEvent);
connect(m_toolButton, &LtsToolButton::dropSuc, this, &LtsDragIcon::dropSuc);
m_toolButton->setIcon(modifyIcon(m_icon));
m_toolButton->setText(m_text);
generateToolTip();
m_toolButton->setIconSize(QSize(static_cast<int>(m_buttonSize), static_cast<int>(m_buttonSize)));
QVBoxLayout *box = new QVBoxLayout(this);
box->setMargin(0);
box->setSpacing(0);
box->addWidget(m_toolButton);
this->setLayout(box);
m_toolButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
if (m_forceFixedSize)
{
m_toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
setFixedSize(QSize(static_cast<int>(m_buttonSize), static_cast<int>(m_buttonSize)));
}
else
{
m_toolButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
}
setStyleToButton();
}
void LtsDragIcon::addToButtonGroup(QButtonGroup *btnGrp, int btnGrpId)
{
btnGrp->addButton(m_toolButton, btnGrpId);
}
void LtsDragIcon::setIcon(const QIcon &icon)
{
m_icon = icon;
(m_toolButton) ? m_toolButton->setIcon(modifyIcon(m_icon)) : initButton();
}
void LtsDragIcon::setMenu(QMenu *menu)
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->setMenu(menu);
}
QMenu *LtsDragIcon::menu() const
{
if (nullptr == m_toolButton)
return nullptr;
return m_toolButton->menu();
}
void LtsDragIcon::setText(const QString &text)
{
m_text = text;
(m_toolButton) ? generateToolTip() : initButton();
}
void LtsDragIcon::setContext(const QString &context)
{
m_context = context;
generateToolTip();
}
void LtsDragIcon::generateToolTip()
{
if (!m_toolButton || m_text.isEmpty())
return;
QString text(m_text);
if (!m_context.isEmpty())
text.prepend(m_context + QLatin1String(" - "));
m_toolButton->setText(m_text);
m_toolButton->setToolTip(text);
}
bool LtsDragIcon::isCheckable()
{
return (m_toolButton) ? m_toolButton->isCheckable() : false;
}
void LtsDragIcon::setDraggable(bool draggable)
{
m_isDraggable = draggable;
m_toolButton->setDraggable(m_isDraggable);
}
void LtsDragIcon::setCheckable(bool checkable)
{
m_toolButton->setCheckable(checkable);
}
bool LtsDragIcon::isChecked() const
{
return (m_toolButton) ? m_toolButton->isChecked() : false;
}
void LtsDragIcon::setChecked(bool checked)
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->setChecked(checked);
}
void LtsDragIcon::setSameWidgetDrop(bool drop)
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->setSameWidgetDrop(drop);
}
bool LtsDragIcon::sameWidgetDrop() const
{
if (nullptr == m_toolButton)
return false;
return m_toolButton->sameWidgetDrop();
}
void LtsDragIcon::addMimeData(const QString &mimeType, const QByteArray &data)
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->addMimeData(mimeType, data);
}
bool LtsDragIcon::removeMimeData(const QString &mimeType)
{
if (nullptr == m_toolButton)
initButton();
return m_toolButton->removeMimeData(mimeType);
}
void LtsDragIcon::removeAllMimeData()
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->removeAllMimeData();
}
void LtsDragIcon::setAcceptDrops(bool on)
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->setAcceptDrops(on);
}
void LtsDragIcon::dragEnterEvent(QDragEnterEvent *e)
{
if (m_toolButton)
m_toolButton->dragEnterEvent(e);
}
void LtsDragIcon::dropEvent(QDropEvent *e)
{
QWidget::dropEvent(e);
}
void LtsDragIcon::setButtonStyle(ButtonStyle style)
{
m_style = style;
setStyleToButton();
}
void LtsDragIcon::setIconSize(ButtonSize buttonSize)
{
m_buttonSize = buttonSize;
(m_toolButton) ? m_toolButton->setIconSize(QSize(static_cast<int>(m_buttonSize), static_cast<int>(m_buttonSize))) : initButton();
}
void LtsDragIcon::setForceFixedSize(bool force)
{
m_forceFixedSize = force;
initButton();
}
void LtsDragIcon::setButtonVisible(bool visible)
{
if (nullptr == m_toolButton)
initButton();
m_toolButton->setVisible(visible);
}
bool LtsDragIcon::buttonVisible() const
{
if (nullptr == m_toolButton)
return false;
return m_toolButton->isVisible();
}
void LtsDragIcon::setAction(QAction *action)
{
QIcon icon = action->icon();
if(!icon.isNull())
m_icon = icon;
m_toolButton->setDefaultAction(action);
this->setObjectName(action->objectName());
}
void LtsDragIcon::changeEvent(QEvent *event)
{
if (event->type() == QEvent::EnabledChange)
emit enabledChanged(isEnabled());
}
void LtsToolButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
m_startPos = event->pos();
QToolButton::mousePressEvent(event);
}
void LtsToolButton::mouseReleaseEvent(QMouseEvent *event)
{
QToolButton::mouseReleaseEvent(event);
}
void LtsToolButton::mouseMoveEvent(QMouseEvent *event)
{
if (!m_isDraggable)
return;
if (event->buttons() & Qt::LeftButton) {
int distance = (event->pos() - m_startPos).manhattanLength();
if (distance >= 5)
{
this->setDown(false);
performDrag(event);
return;
}
}
QToolButton::mouseMoveEvent(event);
}
void LtsToolButton::performDrag(QMouseEvent *)
{
QWidget *item = this;
if (item)
{
LtsDragIcon *dragIcon = nullptr;
QString name;
while (dragIcon == nullptr)
{
if ((!item->objectName().isEmpty()) && (name.isNull()))
{
name = item->objectName();
}
item = item->parentWidget();
if (nullptr == item) return; // Huchhhhhhhh, that should never happen
dragIcon = dynamic_cast<LtsDragIcon*>(item);
}
QMimeData *mimeData = new QMimeData();
mimeData->setText(name);
for(auto it = m_mimeData.cbegin(); it != m_mimeData.cend(); ++it)
mimeData->setData(it.key(), it.value());
if (dragIcon->dropHandlerKey() != 0L)
{
QByteArray data = QByteArray(QByteArray::number(dragIcon->dropHandlerKey()));
mimeData->setData(QLatin1String("DropHandlerPtr"),data);
}
QDrag *drag = new QDrag(item);
drag->setMimeData(mimeData);
QAction *action = defaultAction();
if ((action) && (dragIcon->showDragIcon()))
{
drag->setPixmap(action->icon().pixmap(16,16) );
}
else
{
drag->setPixmap(dragIcon->icon().pixmap(16, 16));
}
Qt::DropAction drop = drag->exec(Qt::CopyAction | Qt::MoveAction);
if (drop == Qt::MoveAction || drop == Qt::CopyAction )
emit dropSuc();
}
}
void LtsToolButton::dragEnterEvent(QDragEnterEvent *event)
{
QWidget *source =
qobject_cast<QWidget *>(event->source());
if (source && source != this) {
event->setDropAction(Qt::MoveAction);
event->accept();
}
QWidget::dragEnterEvent(event);
}
void LtsToolButton::addMimeData(const QString &mimeType, const QByteArray &data)
{
m_mimeData.insert(mimeType,data);
}
bool LtsToolButton::removeMimeData(const QString &mimeType)
{
return (m_mimeData.remove(mimeType) > 0);
}
#define SIZE 40
QIcon LtsDragIcon::modifyIcon(const QIcon &icon)
{
if (m_modification.isEmpty())
return icon;
QPixmap iconImage(icon.pixmap(SIZE*0.8, SIZE*0.8));
QPainter painter(&iconImage);
painter.save();
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing | QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform);
painter.setBrush(QColor("red"));
painter.setPen(QPen(QColor("red"), 3));
QFont f;
f.setFamily(QStringLiteral("Monospace"));
f.setPointSize(12);
f.setBold(true);
painter.setFont(f);
painter.drawText(QRect(0, 0, 28, 28), m_modification); // TODO: working version of this: , QTextOption(Qt::AlignRight | Qt::AlignBottom));
painter.restore();
return iconImage;
}
} // namespace LtsWidgets
#include "lts_drag_icon.moc"
+339
View File
@@ -0,0 +1,339 @@
/**
* @RDE_HEADER@
* Draggable Icon with Text for Menus
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_base.h"
#include "lts_drop_handler.h"
#include "lts_widgets_export.h"
#include <QString>
#include <QIcon>
#include <QWidget>
class QButtonGroup;
class QHBoxLayout;
class QLabel;
class QMenu;
namespace LtsWidgets {
class LtsToolButton;
/**
@brief Tool button with drag & drop functionality
*/
class LTSWIDGETS_EXPORT LtsDragIcon : public QWidget
{
public:
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(QString context READ context WRITE setContext)
Q_PROPERTY(QString identifier READ identifier WRITE setIdentifier)
Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
Q_PROPERTY(LtsWidgets::Designer::ButtonSize eButtonSize READ iconSizeDesigner WRITE setIconSizeDesigner)
Q_PROPERTY(bool eForceFixedSize READ isForceFixedSize WRITE setForceFixedSize)
Q_PROPERTY(bool isCheckable READ isCheckable WRITE setCheckable)
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
Q_PROPERTY(bool showDragIcon READ showDragIcon WRITE setShowDragIcon)
Q_PROPERTY(bool isDraggable READ isDraggable WRITE setDraggable)
//last
Q_PROPERTY(LtsWidgets::Designer::ButtonStyle eStyle READ buttonStyleDesigner WRITE setButtonStyleDesigner)
/**
* @brief Set the ButtonStyle in Designer
* @param ButtonStyle to define the possible IconStyles
*/
void setButtonStyleDesigner(Designer::ButtonStyle t) { setButtonStyle(static_cast<ButtonStyle>(t)); }
/**
* @brief ButtonStyle Getter for Designer
* @return the current ButtonStyle for the DragIcon in the designer
*/
Designer::ButtonStyle buttonStyleDesigner() const { return static_cast<Designer::ButtonStyle>(buttonStyle()); }
/**
* @brief Set the ButtonSize in Designer
* @param ButtonSize
*/
void setIconSizeDesigner(Designer::ButtonSize t) { setIconSize(static_cast<ButtonSize>(t)); }
/**
* @brief OpenType Getter for Designer
* @return the current BUttonSize Value for the DragIcon in the designer
*/
Designer::ButtonSize iconSizeDesigner() const { return static_cast<Designer::ButtonSize>(iconSize()); }
public:
/**
* @brief Constructor for LtsDragIcon
* @param parent for the Widget
*/
explicit LtsDragIcon(QWidget *parent);
virtual ~LtsDragIcon() = default;
/**
* @brief set a QMenu to the Button
* @param QMenu to show
*/
void setMenu(QMenu *menu);
/**
* @brief get QMenu from the Button
* @return QMenu or nullptr if no button or menu is defined
*/
QMenu* menu() const;
/**
* @brief set a text to the Button
* @param QString as text shown
*/
void setText(const QString &text);
/**
* @brief Returns the widget's text value
* @return Current text
*/
QString text() const { return m_text; }
/**
* @brief set a context to the Button
* @param QString as context shown
*/
void setContext(const QString &context);
/**
* @brief Returns the widget's context value
* @return Current context
*/
QString context() const { return m_context; }
/**
* @brief set if the Button should be checkable
* By default, the button is not checkable.
* @param new checkable state
*/
void setCheckable(bool checkable);
/**
* @brief Return if Button is checkable
* @return This property holds whether the button is checkable.
*/
bool isCheckable();
/**
* @brief set if the Button should be draggable
* By default, the button is draggable.
* @param new dragabble state
*/
void setDraggable(bool draggable);
/**
* @brief Return if Button is checkable
* @return This property holds whether the button is checkable.
*/
bool isDraggable() const { return m_isDraggable; }
/**
* @brief (un)check the Button
* Only checkable buttons can be checked. By default, the button is unchecked.
* @param new checked state
*/
void setChecked(bool checked);
/**
* @brief Return the current check state
* @return This property holds whether the button is checked.
*/
bool isChecked() const;
/**
* @brief Use Icon / Default Icon when dragging
* By default, the icon is used.
* @param new checked state
*/
void setShowDragIcon(bool showDragIcon) { m_showDragIcon = showDragIcon; }
bool showDragIcon() const { return m_showDragIcon; }
/**
* @brief set new the SameWidgetDrop State
* drag&drop property to allow sameWidgetDrops
* @param new drop state
*/
void setSameWidgetDrop(bool drop);
/**
* @brief get wether SameWidgetDrop is enabled
* @return the drop state
*/
bool sameWidgetDrop() const;
/**
* @brief Set the ButtonStyle in Code
* @param ButtonStyle to define the possible IconStyles
*/
void setButtonStyle(ButtonStyle style);
/**
* @brief Returns the currently used ButtonStyle Value
* @return Current ButtonStyle
*/
ButtonStyle buttonStyle() const { return m_style; }
/**
* @brief Set the ButtonSize in Code
* @param ButtonSize
*/
void setIconSize(ButtonSize buttonSize);
/**
* @brief Returns the current used ButtonSize Value
* @return Current ButtonSize
*/
ButtonSize iconSize() const { return m_buttonSize; }
/**
* @brief set new visibilty fo the Icon
* @param New visibilty
*/
void setButtonVisible(bool visible);
/**
* @brief set new visibilty fo the Icon
* @param New visibilty
*/
bool buttonVisible() const;
/**
* @brief set Icon to the Button
* @param New QIcon to use
*/
void setIcon(const QIcon &icon);
/**
* @brief Return the current used QIcon
* @return This property holds the current used QIcon.
*/
QIcon icon() const { return m_icon; }
/**
* @brief Set to Force the given IconSize and forbid resizing
* By default, the button resizes into given space
* @param bool to force the Icon to hold it's given
* IconSize even if the LayoutSystem provides more space to fill
*/
void setForceFixedSize(bool force);
/**
* @brief Return the current ForeFixedSize value
* @return This property holds the set ForceFicedSize value
*/
bool isForceFixedSize() const { return m_forceFixedSize; }
/**
* @brief set a QAction to the Button
* @param New QAction
* the new Action will override the old one and replaces
* the ObjectName with the new ActionName
*/
void setAction(QAction *action);
/**
Adds dragButton to a button group
@param btnGrp button group to add dragButton
@param btnGrpId Button identifier in button group
*/
void addToButtonGroup(QButtonGroup *btnGrp, int btnGrpId);
/**
* @brief Sets a dropHandler. The dropHandler must be allocated with new operator. The owner ship now has the LtsDragIcon. The LtsDragIcon calls delete in hier dtor
* @see LtsDropHandler
*/
void setDropHandlerKey(qulonglong key) { m_dropHandlerKey = key; }
/**
* @brief Adds MimeData.
* This sets MimeData to the Drag Action
* @param QString with MimeType Description
* @param QByteArray with MimeData
*/
void addMimeData(const QString &mimeType, const QByteArray &data);
bool removeMimeData(const QString &mimeType);
void removeAllMimeData();
/**
* @brief Sets the drop acceptance of the Icon
* @param bool value to set drop acceptance
*/
void setAcceptDrops(bool on);
qulonglong dropHandlerKey() const { return m_dropHandlerKey; }
// Todo: Isn't this something like objectName?
void setIdentifier(const QString &id) { m_identifier = id; }
QString identifier() const { return m_identifier; }
void setModification(const QString &id) { m_modification = id; setIcon(m_icon); }
QString modification() const { return m_modification; }
void removeModification() { m_modification = QString(); setIcon(m_icon);}
signals:
/**
* @brief The Signal is emitted when the Button is activated
* (i.e. pressed down then released while the mouse cursor is inside the button)
* @param bool checked = false
*/
void clicked();
/**
* @brief The Signal is emitted when the Button is toggled
* @param bool checked
*/
void toggled(bool);
void newDropEvent(QDropEvent *e);
void dropSuc();
void enabledChanged(bool);
protected:
void dragEnterEvent(QDragEnterEvent *e) override;
void dropEvent(QDropEvent *e) override;
void initButton();
private:
void setStyleToButton();
QIcon modifyIcon(const QIcon &icon);
void generateToolTip();
protected slots:
void changeEvent(QEvent *event) override;
private:
QHBoxLayout *m_lay;
LtsToolButton *m_toolButton;
QIcon m_icon;
QLabel *m_dragLabel;
QString m_text;
QString m_identifier;
QString m_modification;
ButtonStyle m_style;
bool m_forceFixedSize;
bool m_isDraggable;
qulonglong m_dropHandlerKey;
ButtonSize m_buttonSize;
bool m_showDragIcon;
QString m_context;
};
}
+141
View File
@@ -0,0 +1,141 @@
/**
**
** @RDE_HEADER@
** Virtual base class of a simple drop handler.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Linux, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 18.12.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
class QMimeData;
namespace LtsWidgets
{
/**
@class LtsDropHandler pdrophandler.h
@brief Interface class to handle drop events of special mime data type.
@see SituationMap::Map, SituationMap::DropEventManager
The LtsDropHandler class is an interface class to handle drop event for one type of mime data. It is used from DropEventManager to process drop events.
The DropEventManager will call the doDrop function to process events. The doDrop fuction has to be implemented in derived class.
Following example shows how to implement a drop handler.
@code
#include <pwidgets/pdrophandler.h>
class DropCreatePolyLine : public LtsWidgets::LtsDropHandler
{
PlanEditor * m_planEditor;
PlanEditor::InternalData * m_planEditorInternal;
bool m_smooth;
public:
DropCreatePolyLine( PlanEditor * planEditor
, PlanEditor::InternalData * planEditorInternal
, bool smooth = false )
: LtsWidgets::LtsDropHandler()
, m_planEditor(planEditor)
, m_planEditorInternal(planEditorInternal)
, m_smooth(smooth) {;}
virtual ~DropCreatePolyLine() = default;
void doDrop( int x, int y, const QMimeData * mimeData = nullptr) const override;
}; // DropCreatePolyLine
void PlanEditor::installLineDropHandler( LtsWidgets::LtsDragIcon * dragIcon )
{
qulonglong key = m_dropEventManager->add(new DropCreatePolyLine(this,m_internal));
dragIcon->setDropHandlerKey( key );
} // PlanEditor::installLineDropHandler( LtsWidgets::LtsDragIcon * dragIcon )
void DropCreatePolyLine::doDrop( int x, int y, const QMimeData * mimeData ) const
{
SituationMap::Map * map;
SituationMap::RB::RubberbandMultiPointFeature * rubberband;
SituationMap::RB::RubberbandFactory & rbFactory;
map = m_planEditorInternal->m_map;
rbFactory = map->rubberbandFactory();
if (m_smooth) rubberband = rbFactory.create<SituationMap::RB::RubberbandSmoothLine>();
else rubberband = rbFactory.create<SituationMap::RB::RubberbandMultiPointFeature>();
if (rubberband != nullptr)
{
rubberband -> setAutoDelete( true );
rubberband -> enableClosed( false );
uint dx = map->width() * 0.25;
uint dy = map->height() * 0.25;
QVector<QPointF> lineCoordinates;
lineCoordinates += map->viewPortWindowPointToGeoCoordinate(QPoint(x-dx,y-dy));
lineCoordinates += map->viewPortWindowPointToGeoCoordinate(QPoint(x+dx,y+dy));
rubberband->setCoordinateVector( lineCoordinates );
QObject::connect( rubberband , &SituationMap::RB::Rubberband::apply
, m_planEditor, &PlanEditor::onPolylineCreate );
QObject::connect( rubberband , &SituationMap::RB::Rubberband::abort
, m_planEditor, &PlanEditor::onRubberbandAbort );
} // DropCreatePolyLine::doDrop( int x, int y )
} // DropCreatePolyLine::doDrop( int x, int y ) const
@endcode
*/
class LtsDropHandler
{
public:
LtsDropHandler() = default;
virtual ~LtsDropHandler() = default;
LtsDropHandler( const LtsDropHandler & ) = delete;
LtsDropHandler & operator = ( const LtsDropHandler & ) = delete;
/**
Function to process drop events.
@param x x coordinate of cursor position referred to widget pixel coordinates.
@param y y coordinate of cursor position referred to widget pixel coordinates.
@param mimeData Pointer to data to be dropped.
*/
virtual void doDrop( int x, int y, const QMimeData * mimeData ) const = 0;
}; // class LtsDropHandler
} // namespace LtsWidgets
+66
View File
@@ -0,0 +1,66 @@
/**
* @RDE_HEADER@
* LtsWidget - a QLabel which elides the text if necessary
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, NLE, 2018-12-17
*/
#include "lts_elided_label.h"
#include <QPaintEvent>
#include <QResizeEvent>
namespace LtsWidgets
{
void LtsElidedLabel::setElideMode(Qt::TextElideMode elideMode)
{
m_elideMode = elideMode;
m_cachedText.clear();
update();
}
void LtsElidedLabel::resizeEvent(QResizeEvent *e)
{
QLabel::resizeEvent(e);
m_cachedText.clear();
}
void LtsElidedLabel::paintEvent(QPaintEvent *e)
{
if (m_elideMode == Qt::ElideNone)
return QLabel::paintEvent(e);
updateCachedTexts();
QLabel::setText(m_cachedElidedText);
QLabel::paintEvent(e);
QLabel::setText(m_cachedText);
}
void LtsElidedLabel::updateCachedTexts()
{
// setText() is not virtual ... :/
const auto txt = text();
if (m_cachedText == txt)
return;
m_cachedText = txt;
const QFontMetrics fm(fontMetrics());
m_cachedElidedText = fm.elidedText(text(),
m_elideMode,
width(),
Qt::TextShowMnemonic);
if (!m_cachedText.isEmpty())
{
const QString showFirstCharacter = m_cachedText.at(0) + QStringLiteral("...");
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
const int width = fm.width(showFirstCharacter);
#else
const int width = fm.horizontalAdvance(showFirstCharacter);
#endif
setMinimumWidth(width + 1);
}
}
} // namespace LtsWidgets
+42
View File
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* LtsWidget - a QLabel which elides the text if necessary
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, NLE, 2018-12-17
*/
#pragma once
#include <QLabel>
#include <LtsWidgets/lts_widgets_export.h>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsElidedLabel : public QLabel
{
Q_OBJECT
public:
Q_PROPERTY(Qt::TextElideMode ElideMode READ elideMode WRITE setElideMode)
using QLabel::QLabel;
void setElideMode(Qt::TextElideMode elideMode);
Qt::TextElideMode elideMode() const { return m_elideMode; }
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void updateCachedTexts();
private:
Qt::TextElideMode m_elideMode = Qt::ElideRight;
QString m_cachedElidedText;
QString m_cachedText;
};
} // namespace LtsWidgets
+63
View File
@@ -0,0 +1,63 @@
/**
* @RDE_HEADER@
* Events provided by LtsWidgets
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_events.h"
namespace LtsWidgets
{
QEvent::Type LtsLinkEvent::eventTypeId()
{
static QEvent::Type sLinkEventId = QEvent::None;
if (sLinkEventId == QEvent::None)
sLinkEventId = static_cast<QEvent::Type>(QEvent::registerEventType());
return sLinkEventId;
}
QEvent::Type SaveImageEvent::saveImageEventId()
{
static QEvent::Type sSaveImageEventId = QEvent::None;
if (sSaveImageEventId == QEvent::None)
sSaveImageEventId = static_cast<QEvent::Type>(QEvent::registerEventType());
return sSaveImageEventId;
}
QEvent::Type SetMarkerEvent::setMarkerEventId()
{
static QEvent::Type sSetMarkerEventId = QEvent::None;
if (sSetMarkerEventId == QEvent::None)
sSetMarkerEventId = static_cast<QEvent::Type>(QEvent::registerEventType());
return sSetMarkerEventId;
}
QEvent::Type AddMarkerEvent::addMarkerEventId()
{
static QEvent::Type sAddMarkerEventId = QEvent::None;
if (sAddMarkerEventId == QEvent::None)
sAddMarkerEventId = static_cast<QEvent::Type>(QEvent::registerEventType());
return sAddMarkerEventId;
}
QEvent::Type AddSnapshotEvent::addSnapshotEventId()
{
static QEvent::Type eventId = QEvent::None;
if (eventId == QEvent::None)
eventId = static_cast<QEvent::Type>(QEvent::registerEventType());
return eventId;
}
QEvent::Type SaveSnapshotEvent::saveSnapshotEventId()
{
static QEvent::Type eventId = QEvent::None;
if (eventId == QEvent::None)
eventId = static_cast<QEvent::Type>(QEvent::registerEventType());
return eventId;
}
} // namespace LtsWidgets
+99
View File
@@ -0,0 +1,99 @@
/**
* @RDE_HEADER@
* Events provided by LtsWidgets
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_widgets_export.h"
#include <QEvent>
#include <QPixmap>
#include <QPointer>
#include <QWidget>
#include <QUrl>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsLinkEvent : public QEvent
{
public:
explicit LtsLinkEvent(QUrl url, QWidget *sourceBaseLayoutWidget)
: QEvent(eventTypeId())
, m_url(std::move(url))
, m_sourceBaseLayoutWidget(sourceBaseLayoutWidget)
{}
static QEvent::Type eventTypeId();
QWidget *sourceBaseLayoutWidget() const { return m_sourceBaseLayoutWidget.data(); }
QUrl url() const { return m_url; }
private:
QUrl m_url;
QPointer<QWidget> m_sourceBaseLayoutWidget;
};
class LTSWIDGETS_EXPORT SaveImageEvent : public QEvent
{
public:
SaveImageEvent(QWidget *w, QPixmap &&pm)
: QEvent(saveImageEventId()), m_widget(w), m_pix(std::move(pm))
{}
const QPixmap &pixmap() const { return m_pix; }
QWidget *widget() const { return m_widget.data(); }
static QEvent::Type saveImageEventId();
private:
QPointer<QWidget> m_widget;
QPixmap m_pix;
};
class LTSWIDGETS_EXPORT AddMarkerEvent : public QEvent
{
public:
AddMarkerEvent()
: QEvent(addMarkerEventId())
{}
static QEvent::Type addMarkerEventId();
};
class LTSWIDGETS_EXPORT SetMarkerEvent : public QEvent
{
public:
SetMarkerEvent(const QString &description)
: QEvent(setMarkerEventId()), m_description(description)
{}
const QString &getDescription() const { return m_description; }
void setDescription(const QString &description) { m_description = description; }
static QEvent::Type setMarkerEventId();
private:
QString m_description;
};
class LTSWIDGETS_EXPORT AddSnapshotEvent : public QEvent
{
public:
AddSnapshotEvent()
: QEvent(addSnapshotEventId())
{}
static QEvent::Type addSnapshotEventId();
};
class LTSWIDGETS_EXPORT SaveSnapshotEvent : public QEvent
{
public:
SaveSnapshotEvent(const QString &description)
: QEvent(saveSnapshotEventId()), m_description(description)
{}
const QString &getDescription() const { return m_description; }
void setDescription(const QString &description) { m_description = description; }
static QEvent::Type saveSnapshotEventId();
private:
QString m_description;
};
} // namespace LtsWidgets
+160
View File
@@ -0,0 +1,160 @@
/**
* @RDE_HEADER@
* FrameLess Dialog with alternatie basecolor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#include "lts_frameless_dialog.h"
#include <QApplication>
#include <QRect>
#include <QStyle>
#include <QSize>
#include <QScreen>
#include <QScroller>
#include <QScrollArea>
#include <QDesktopWidget>
#include <QShowEvent>
#include <QTimer>
namespace LtsWidgets
{
LtsFramelessDialog::LtsFramelessDialog(QWidget *parent, const Behaviour &behaviour)
: QDialog(parent)
{
// Qt-Bug? Qt::FramelessWindowHint in the underlying windows results in
// mapToGlobal(QPoint(0,0)) == QPoint(0,0) when a popup window is show on top of it
Qt::WindowFlags wf; // TODO: FIX WM AFTER FAT!! = Qt::FramelessWindowHint; // make frameless, composite manager drops the shadow
// frameless dialogs cannot have (completety working) children dialogs => frames 4 fat
switch (behaviour)
{
case Behaviour::Modal:
{
#ifndef TABLET_MODE
setModal(true);
wf |= Qt::Dialog;
#else
wf |= Qt::Window;
#endif
wf |= Qt::WindowStaysOnTopHint;
break;
}
case Behaviour::Popup:
wf |= Qt::Popup;
break;
}
setWindowFlags(wf);
QTimer *t = new QTimer(this);
t->setInterval(250);
connect(t, &QTimer::timeout,
this, [this]()
{
//qWarning() << "my pos" << QCursor::pos();
//qWarning() << "my screen rect" << screenRectFromPoint(QCursor::pos());
QSize s = size();
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, screenRectFromPoint(QCursor::pos())));
});
//t->start(); TODO: impl this!
}
LtsFramelessDialog::~LtsFramelessDialog()
{
}
QRect LtsFramelessDialog::screenRectFromPoint(const QPoint &pos) const
{
const auto screens = QGuiApplication::screens();
for (QScreen *screen : screens)
{
QRect s = screen->geometry().adjusted(-50, -50, 50, 50); // graceful offset for desktop managers
bool contains = s.contains(pos);
//qWarning() << "screen" << s << " widget" << w << " contains?" << contains;
//qWarning() << "screen: x" << s.x() << "y" << s.y() << "h" << s.height() << "w" << s.width();
//qWarning() << "widget: x" << w.x() << "y" << w.y() << "h" << w.height() << "w" << w.width();
if (contains)
return screen->geometry();
}
return QRect();
}
void LtsFramelessDialog::showEvent(QShowEvent *event)
{
QDialog::showEvent(event);
QTimer::singleShot(0, this, &LtsFramelessDialog::onUpdateGeometry);
#ifdef TABLET_MODE
QList<QWidget *> scrollWidgets = findChildren<QWidget *>();
for (auto i = scrollWidgets.begin(); i != scrollWidgets.end(); ++i)
{
QWidget *widget = *i;
QVariant isConst = widget->property("const");
if (isConst.isValid() && isConst.toBool())
continue;
addTouchScroll(widget);
}
#endif
}
void LtsFramelessDialog::addTouchScroll(QWidget *widget)
{
if (!widget)
return;
widget->setAttribute(Qt::WA_AcceptTouchEvents);
QScrollArea *sca = qobject_cast<QScrollArea *>(widget);
if (sca)
{
QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture);
return;
}
}
void LtsFramelessDialog::onUpdateGeometry()
{
QRect r;
QWidget *p = parentWidget();
if (p) // get the parent's window and center the dialog on the center of the parent's window
{
QWidget *window = p->window();
QPoint global = window->mapToGlobal(QPoint(0,0));
QSize size = window->size();
r = QRect(global, size);
}
else // no parent - no alignment!
{
r = QGuiApplication::primaryScreen()->geometry();
}
QSize s = size();
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, r));
}
} // namespace LtsWidgets
+61
View File
@@ -0,0 +1,61 @@
/**
* @RDE_HEADER@
* FrameLess Dialog with alternatie basecolor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#pragma once
#include <QDialog>
#include "lts_base.h"
#include "lts_widgets_export.h"
namespace LtsWidgets
{
/**
* @brief The Behaviour enum describes how the FramelessDialog handles user input while shown.
*/
enum class Behaviour
{
Popup = 0, // a mouse click on any surface other than this dialog will close/hide this dialog
Modal, // disallow any input, but do not block the program execution (does not apply when exec() is used), and avoid rejecting when dialog is modal
};
/**
* @brief The LtsFramelessDialog class creates a Popup-Dialog, without a frame, but with a shadow as border. This Popup-Dialog is centered in the window .
* The purpose is to cover the window the user is working with, to interupt the user with a message he has to attend first before continuing work.
* LtsFramelessDialog is actually a QWidget as this class uses a QWidget which casts a shadow and lays itself on it. Sorry.
* The Application must never be interrupted, so there is no exec().
*/
class LTSWIDGETS_EXPORT LtsFramelessDialog : public QDialog // TODO: many methods use this to show messages, use LtsMessageDialog instead
{
Q_OBJECT
public:
/**
* @brief LtsFramelessDialog
* @param parent the widget to be covered
* @param behaviour see enum LtsWidgets::Behaviour - only affects this Dialog when currently shown
*/
explicit LtsFramelessDialog(QWidget *parent = nullptr, const Behaviour &behaviour = Behaviour::Popup);
virtual ~LtsFramelessDialog();
public Q_SLOTS:
void onUpdateGeometry();
private:
QRect screenRectFromPoint(const QPoint &pos) const;
void addTouchScroll(QWidget *widget);
protected:
void showEvent(QShowEvent *event) override;
};
} // namespace LtsWidgets
+225
View File
@@ -0,0 +1,225 @@
// TODO: hdr
#include "lts_global_wait_dialog.h"
#include "lts_progress_dialog_helper.h"
#include "lts_overlay_loading.h"
#include "lts_dialog.h"
#include "lts_frameless_dialog.h"
#include <QCoreApplication>
#include <QTimer>
//#include <QDebug>
#ifdef Q_OS_UNIX
#include <execinfo.h> // backtrace(..)
#include <dlfcn.h> // dladdr(..)
#include <cxxabi.h> // demangle
#endif
namespace LtsWidgets
{
LtsGlobalWaitDialog::~LtsGlobalWaitDialog()
{
delete m_progressDialog;
}
LtsGlobalWaitDialog::LtsGlobalWaitDialog()
: m_waitingCount(0)
, m_visible(true)
, m_progressDialog(new LtsProgressDialogHelper(nullptr))
, m_hideTimer(new QTimer(this)) // avoids blinking windows by waiting a little until the wait window will be hidden
{
m_hideTimer->setSingleShot(true);
m_hideTimer->setInterval(500);
connect(m_hideTimer, &QTimer::timeout,
this, &LtsGlobalWaitDialog::hideIt);
}
void LtsGlobalWaitDialog::unarm()
{
m_armed = false;
hideIt(); // force hide it (don't worry, does nothing if now shown)
}
void LtsGlobalWaitDialog::rearm()
{
m_armed = true;
setVisible(true); // show again (if some component is still waiting)
}
void LtsGlobalWaitDialog::setVisible(bool visible)
{
if (m_waitingCount > 0 && visible)
showIt();
if (m_waitingCount < 1 && !visible)
hideIt();
}
void LtsGlobalWaitDialog::showIt()
{
if (m_progressDialog->isShown())
return;
m_hideTimer->stop();
if (m_armed)
{
m_progressDialog->slotShowWaitForReplyWindow();
}
}
void LtsGlobalWaitDialog::hideIt()
{
if (!m_progressDialog->isShown())
return;
m_hideTimer->stop();
m_progressDialog->slotStopWaitForReplyWindow();
}
void bt()
{
#ifdef Q_OS_UNIX
void *callstack[128];
const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
int nFrames = backtrace(callstack, nMaxFrames);
char **symbols = backtrace_symbols(callstack, nFrames);
for (int i = 1; i < nFrames; i++)
{
QString address(QStringLiteral("0x%1").arg(QString::number(*(quintptr *)callstack[i], 16)));
QString function;
int offset = -1; // aka. invalid
Dl_info info;
if (dladdr(callstack[i], &info) && info.dli_sname)
{
char *demangled = nullptr;
int status = -1;
if (info.dli_sname[0] == '_')
demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status);
function = QString::fromUtf8(status == 0 ? demangled : info.dli_sname == 0 ? symbols[i] : info.dli_sname);
offset = (char *)callstack[i] - (char *)info.dli_saddr;
free(demangled);
}
else
{
function = QString::fromUtf8(symbols[i]);
}
//qWarning() << "frame" << i << address << function << offset;
}
free(symbols);
// if (nFrames == nMaxFrames)
// qWarning() << "[BACKTRACE TRUNCATED]";
#endif
}
void LtsGlobalWaitDialog::wait(QWidget *parent, const QString &customText)
{
m_progressDialog->setParentWidget(parent);
if (m_waitingCount < 1 && m_visible) // nothing is waiting
showIt();
setCustomText(customText);
m_waitingCount++; // count even if not visible !
//qWarning() << "LtsGlobalWaitDialog ++" << m_waitingCount;
//bt();
}
void LtsGlobalWaitDialog::setCustomText(const QString &customText)
{
m_progressDialog->setCustomText(customText);
}
void LtsGlobalWaitDialog::done()
{
if (m_waitingCount == 0)
{
//qWarning() << "LtsGlobalWaitDialog: called done() while there is nothing waiting!";
m_hideTimer->start();
return;
}
Q_ASSERT(m_waitingCount > 0);
m_waitingCount--;
//qWarning() << "LtsGlobalWaitDialog --" << m_waitingCount;
if (m_waitingCount == 0) // no more waiting classes
m_hideTimer->start();
}
static LtsGlobalWaitDialog *s_gwdInstance = nullptr;
void LtsGlobalWaitDialog::cleanupGwdInstance()
{
delete s_gwdInstance;
s_gwdInstance = nullptr;
}
LtsGlobalWaitDialog *LtsGlobalWaitDialog::getInstance()
{
if (!s_gwdInstance)
{
s_gwdInstance = new LtsGlobalWaitDialog;
qAddPostRoutine(&LtsGlobalWaitDialog::cleanupGwdInstance);
}
return s_gwdInstance;
}
} // namespace LtsWidgets
/// Waiter
Waiter::Waiter(const QString &customText)
{
LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(nullptr, customText);
}
Waiter::Waiter(LtsWidgets::LtsDialog *parent, const QString &customText)
: m_parent(parent)
{
m_oldClosableState = m_parent->isClosable();
m_parent->setClosable(false);
m_parent->setCurrentlyBlocking(true);
auto *wait = new LtsWidgets::LtsOverlayLoading;
wait->setText(customText.isEmpty() ? QObject::tr("Waiting for server reply...") : customText);
m_parent->setOverlay(wait);
m_loadingOverlay = wait;
}
Waiter::~Waiter()
{
if (m_loadingOverlay == nullptr)
{
LtsWidgets::LtsGlobalWaitDialog::getInstance()->done();
return;
}
m_parent->setClosable(m_oldClosableState);
m_parent->setCurrentlyBlocking(false);
m_loadingOverlay->deleteLater();
}
+77
View File
@@ -0,0 +1,77 @@
/**
* @RDE_HEADER@
* Frameless Dialog, modal, exists exactly once per - a global/static wait dlg which counts for all compontens that are waiting for their operation to be done but don't want to own a progress dialog !
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-17
*/
#pragma once
#include "lts_widgets_export.h"
#include <QObject>
class QTimer;
class QWidget;
namespace LtsWidgets
{
class LtsDialog;
class LtsOverlayLoading;
class LtsProgressDialogHelper;
/**
* @brief The LtsGlobalWaitDialog class is a global/static wait dlg which counts for all compontens that are waiting for their operation to be done but don't want to own a progress dialog !
*/
class LTSWIDGETS_EXPORT LtsGlobalWaitDialog : public QObject
{
Q_OBJECT
public:
static LtsGlobalWaitDialog* getInstance();
void wait(QWidget *parent, const QString &customText = QString()); // shows the Progress Dialog
void wait(const QString &customText = QString()) { wait(nullptr, customText); }
void done(); // hides the Progress Dialog IF all wait()s were canceled with done() !
void setVisible(bool visible = true);
void setCustomText(const QString &customText = QString()); // call with empty string to reset text to default text "waiting for server"
void unarm(); // deactivate the global wait dialog from showing up (e.g. wait dialog should not be recorded while recording a script in the script editor)
void rearm(); // undo the unarm (re-activate global wait dialog)
private:
friend struct LtsGlobalWaitDialogHandler;
LtsGlobalWaitDialog();
~LtsGlobalWaitDialog();
static void cleanupGwdInstance();
void showIt();
void hideIt();
int m_waitingCount;
bool m_visible;
bool m_armed { true };
LtsProgressDialogHelper *m_progressDialog;
QTimer *m_hideTimer;
};
} // namespace LtsWidgets
// 'hyper-global' stuff into main namespace
class LTSWIDGETS_EXPORT Waiter
{
public:
explicit Waiter(const QString &customText = {});
explicit Waiter(LtsWidgets::LtsDialog *parent, const QString &customText = {});
~Waiter();
private:
LtsWidgets::LtsOverlayLoading *m_loadingOverlay { nullptr };
LtsWidgets::LtsDialog *m_parent { nullptr };
bool m_oldClosableState { false };
};
+135
View File
@@ -0,0 +1,135 @@
/**
* @RDE_HEADER@
* GridLaout with auto column layouter.
* Layouts all widgets depending on the maxColumn value
* releayouts if maxColumn changed
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_grid_layout.h"
#include <QResizeEvent>
namespace LtsWidgets
{
LtsGridLayout::LtsGridLayout(LtsWidgets::ButtonSize buttonSize, QWidget *parent, int maxColumn)
: QGridLayout(parent)
, m_buttonSize(buttonSize)
, m_defaultMaxColumn(maxColumn)
, m_maxColumn(maxColumn)
, m_colSize(0)
, m_autoSize(maxColumn < 0)
{
setSizeConstraint(SizeConstraint::SetNoConstraint);
//setHorizontalSpacing(1);
setVerticalSpacing(1);
setMargin(0);
}
void LtsGridLayout::add_widget(QWidget *widget)
{
for (auto it = m_widgetList.cbegin(); it != m_widgetList.cend(); ++it)
{
if (*it == widget)
return;
}
connect(widget, &QWidget::destroyed, this, &LtsGridLayout::onWidgetDestroyed);
addWidgetInternal(widget);
m_widgetList.push_back(widget);
}
void LtsGridLayout::setAutoSize(bool enable)
{
m_autoSize = enable;
if (m_autoSize == false)
setMaxColumn(1);
else
{
m_maxColumn = -1;
m_oldSize = QSize();
m_oldGeo = QRect();
QResizeEvent e(sizeHint(), sizeHint());
onParentResize(&e);
setGeometry(QRect(0, 0, sizeHint().width(), sizeHint().height()));
}
}
void LtsGridLayout::setMaxColumn(int max)
{
m_maxColumn = max;
m_defaultMaxColumn = max;
//resetInternalSize
m_colSize = 0;
for (QWidget *wid : qAsConst(m_widgetList))
removeWidget(wid);
for (QWidget *wid : qAsConst(m_widgetList))
addWidgetInternal(wid);
}
void LtsGridLayout::addWidgetInternal(QWidget *widget)
{
int current_row = 0;
int current_column = 0;
while (QLayoutItem *item = itemAtPosition(current_row, current_column))
{
if (!item->widget())
{
removeItem(item);
continue;
}
if (current_column == (m_maxColumn - 1))
{
current_column = 0;
++current_row;
}
else
++current_column;
}
if (current_row == 0)
m_colSize += 1;
QGridLayout::addWidget(widget, current_row, current_column);
}
void LtsGridLayout::onParentResize(QResizeEvent *e)
{
if (m_autoSize && m_oldSize != e->size())
{
bool bigger = (e->size().width() > m_oldSize.width());
m_oldSize = e->size();
int newWidth = m_oldSize.width();
int maxIconsInRow = newWidth / (static_cast<int>(m_buttonSize) + 4);
if (!bigger && newWidth <= (static_cast<int>(m_buttonSize)*m_colSize - static_cast<int>(m_buttonSize) / 2))
maxIconsInRow--;
if (m_maxColumn != maxIconsInRow)
setMaxColumn(maxIconsInRow);
}
}
void LtsGridLayout::onWidgetDestroyed()
{
QWidget *s = qobject_cast<QWidget*>(sender());
for (auto it = m_widgetList.begin(); it != m_widgetList.end(); ++it)
{
if (*it == s)
{
QGridLayout::removeWidget(s);
m_widgetList.erase(it);
setMaxColumn(m_maxColumn); // resort, also remove the layoutitem
break;
}
}
}
} // namespace LtsWidgets
+92
View File
@@ -0,0 +1,92 @@
/**
* @RDE_HEADER@
* GridLaout with auto column layouter.
* Layouts all widgets depending on the maxColumn value
* releayouts if maxColumn changed
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QGridLayout>
#include <QVector>
#include "lts_widgets_export.h"
#include "lts_base.h"
class QWidget;
namespace LtsWidgets
{
/**
* @brief The LtsGridLayout class provides a LayoutItem for the Lts Layout System and is the equivalent to the QGridLayout.
* An LtsGridLayout can be added to a Widget as any other LayoutItem
* A LtsGridLayout got two modes:
* The first one is the normal operation Mode with a fixed ColumnSize
* every time a widget is added wit add_widget (not addWidget!)
* it will use the next column at the current used row
* when maxColum is reached in the current row
* the Widget will be added to a new row
*
* The Second Mode is enabeled with AutoSize
* There the MaxColumnSize will be calculated
* when a rezize event accure
*/
class LTSWIDGETS_EXPORT LtsGridLayout : public QGridLayout
{
Q_OBJECT
public:
/**
* @brief Constructor for LtsGridLayout
* @param ButtonSize for calcualting Gridzize on autoSize
* @param Parent widget
* @param max Column to use
*/
explicit LtsGridLayout(LtsWidgets::ButtonSize buttonSize, QWidget *parent = nullptr, int maxColumn = 1);
/**
* @brief adds a QWidget to the Layout in the current Row
* if MaxColumn is reached, the widget will be added to a new row
* @param QWidget addet to the Layout
*/
void add_widget(QWidget *widget);
/**
* @brief enables autoSize
* calculates on rezize the MaxColum count for given ButtonSize
* @param enable autoSize
*/
void setAutoSize(bool enable);
/**
* @brief set MaxColumn
* @param int maxColum count
*/
void setMaxColumn(int max);
private:
void addWidgetInternal(QWidget *widget);
public Q_SLOTS:
void onParentResize(QResizeEvent *e);
private Q_SLOTS:
void onWidgetDestroyed();
private:
LtsWidgets::ButtonSize m_buttonSize;
int m_defaultMaxColumn;
int m_maxColumn;
int m_colSize;
QVector<QWidget *> m_widgetList;
QRect m_oldGeo;
QSize m_oldSize;
bool m_autoSize;
};
} // namespace
+627
View File
@@ -0,0 +1,627 @@
/**
* @RDE_HEADER@
* Widget that visualizes the topography of a bunch of points along a rubberband
* $Id$
* $HeadURL$
* $Author$
* Created by Malte Schroeder, NLSE, 2018-03-07
*/
#include "lts_height_plot_widget.h"
#include <QPainter>
#include <QPainterPath>
#include <QPen>
#include <QTransform>
#include <QMouseEvent>
#include <cmath>
static inline double getLength(const QPointF &p1, const QPointF &p2)
{
const double x = p1.x() - p2.x();
const double y = p1.y() - p2.y();
return sqrt(x * x + y * y);
}
class LtsHeightPlotWidget::Private
{
public:
Private()
: m_length(0)
, m_minElevation(0)
, m_maxElevation(0)
, m_nullX(0)
, m_nullY(0)
, m_scaleX(0)
, m_scaleY(0)
, m_fillBrush(Qt::gray)
, m_axisPen(Qt::black)
, m_mousePosPen(Qt::blue)
, m_markerPen(Qt::magenta)
, iPositionHint(1)
{
m_axisPen.setWidth(1);
m_viewLinePen.setWidth(1);
m_textFont.setPixelSize(10);
m_markerPen.setWidth(2);
m_markerPen.setCapStyle(Qt::RoundCap);
m_mousePosPen.setStyle(Qt::DashDotDotLine);
m_viewLinePen.setStyle(Qt::DashLine);
}
void clearPixmap();
double getDataHeight(double pos) const;
QPointF getDataPos(double pos) const;
int findDataPointIdx(const QPointF &point) const;
void clear();
void recalcData();
void setData(const QVector<QPair<QPointF, double> > &data);
void setMarks(const QVector<QPointF> &data);
void resize(const QSize &newSize);
void repaint();
void paintLines(QPainter &p) const;
QVector< QPair< QPointF, double > > m_data;
QVector< QPointF > m_marks;
QVector< QPair< double, double > > m_heightList; // Laenge, Hoehe
double m_length;
double m_minElevation;
double m_maxElevation;
double m_nullX;
double m_nullY;
double m_scaleX;
double m_scaleY;
QPainterPath m_drawPoints; // PainterPath der Daten, unskaliert
QBrush m_fillBrush;
QPen m_axisPen;
QPen m_viewLinePen;
QPen m_mousePosPen;
QPen m_markerPen;
QFont m_textFont;
QPointF m_mousePos;
QPointF m_mouseLeftClickPos;
QSize m_curSize; // aktuelle Groesse des Widgets
QTransform m_trans1; // nur Verschiebung des Koordinatensystem
QTransform m_trans2; // Translation Pixel <-> Daten
QTransform m_transInverted; // m_trans1.inverted() * m_trans2.inverted()
QTransform m_transCombined; // m_trans2 * m_trans1
QPixmap m_pixmap;
mutable int iPositionHint;
};
double LtsHeightPlotWidget::Private::getDataHeight(double pos) const
{
if (m_heightList.count() <= 1)
return 0;
// boundary checks
if (pos <= 0.)
return m_heightList[0].second;
if (pos >= m_heightList[m_heightList.count() - 1].first)
return m_heightList[m_heightList.count() - 1].second;
if (iPositionHint >= m_heightList.size())
iPositionHint = 1;
if (pos <= m_heightList[iPositionHint].first)
{
while (iPositionHint > 1 && pos <= m_heightList[iPositionHint - 1].first)
--iPositionHint;
}
else
{
while (iPositionHint < m_heightList.count() - 1 && pos > m_heightList[iPositionHint].first)
++iPositionHint;
}
const int iFound = iPositionHint;
// y = m*x + n
const double x = m_heightList[iFound].first - m_heightList[iFound - 1].first;
const double y = m_heightList[iFound].second - m_heightList[iFound - 1].second;
const double m = y / x;
const double n = m_heightList[iFound].second - m * m_heightList[iFound].first;
return m * pos + n;
}
QPointF LtsHeightPlotWidget::Private::getDataPos(double pos) const
{
if (m_heightList.count() <= 1)
return QPointF();
// boundary checks
if (pos <= 0.)
return m_data[0].first;
if (pos >= m_heightList[m_heightList.count() - 1].first)
return m_data[m_heightList.count() - 1].first;
if (pos <= m_heightList[iPositionHint].first)
{
while (iPositionHint > 1 && pos <= m_heightList[iPositionHint - 1].first)
--iPositionHint;
}
else
{
while (iPositionHint < m_heightList.count() - 1 && pos > m_heightList[iPositionHint].first)
++iPositionHint;
}
const int iFound = iPositionHint;
// pos = pos1 + pos1 * prozent(p1,p2)
const double diff = m_heightList[iFound].first - m_heightList[iFound - 1].first;
double percentile = (pos - m_heightList[iFound - 1].first) / diff;
const QPointF &p1 = m_data[iFound - 1].first;
const QPointF p2 = m_data[iFound].first - m_data[iFound - 1].first;
return QPointF(p1.x() + p2.x() * percentile, p1.y() + p2.y() * percentile);
}
int LtsHeightPlotWidget::Private::findDataPointIdx(const QPointF &point) const
{
for (int i = 0; i < m_heightList.count() - 1; ++i)
{
// y = m*x + n
const double x = m_data[i + 1].first.x() - m_data[i].first.x();
const double y = m_data[i + 1].first.y() - m_data[i].first.y();
const double m = y / x;
const double n = m_data[i].first.y() - m * m_data[i].first.x();
// Punkt testen ob er auf der Linie liegt
const double yRes = m * point.x() + n;
if (fabs(yRes - point.y()) < 1.)
{
// und schauen ob er nicht hinter dem zweiten Punkt liegt
const double len = getLength(m_data[ i ].first, point);
if (len <= m_heightList[i].first)
return i;
}
}
return -1;
}
void LtsHeightPlotWidget::Private::clear()
{
m_data.clear();
m_marks.clear();
m_length = 0;
m_minElevation = 0;
m_maxElevation = 0;
m_scaleX = 0;
m_scaleY = 0;
m_drawPoints = QPainterPath();
m_heightList.clear();
m_curSize = QSize();
iPositionHint = 1;
}
void LtsHeightPlotWidget::Private::clearPixmap()
{
m_pixmap = QPixmap();
}
inline double log5(double x)
{
return log10(x) / log10(5.);
}
void LtsHeightPlotWidget::Private::recalcData()
{
m_heightList.clear();
m_drawPoints = QPainterPath();
if (m_data.isEmpty())
{
clear();
return;
}
double length = 0;
double minElevation = m_data.at(0).second;
double maxElevation = minElevation;
for (int i = 1; i < m_data.count(); ++i)
{
length += getLength(m_data.at(i - 1).first, m_data.at(i).first);
minElevation = std::min(minElevation, m_data.at(i).second);
maxElevation = std::max(maxElevation, m_data.at(i).second);
}
// some sanity checks:
if (minElevation < -10000.0) minElevation = -10000.0;
if (maxElevation < -10000.0) maxElevation = -10000.0;
if (minElevation > 10000.0) minElevation = 10000.0;
if (maxElevation > 10000.0) maxElevation = 10000.0;
if (qFuzzyCompare(minElevation, maxElevation))
{
minElevation -= 1.0;
maxElevation += 1.0;
}
// Laenge auf 5^(n-1) m runden
double n2 = pow(5, floor(log5(fabs(minElevation))) - 1);
if (qFuzzyIsNull(n2))
n2 = 0.1; // avoid division by zero
m_minElevation = lrint(floor(minElevation / n2) * n2);
m_maxElevation = lrint(ceil(maxElevation / n2) * n2);
m_scaleY = pow(10, floor(log10(m_maxElevation - m_minElevation)) - 1);
if (m_scaleY < 0.1)
m_scaleY = 0.1;
m_nullY = m_minElevation;
if (m_minElevation < 0 && m_maxElevation > 0)
m_nullY = 0;
// Laenge auf 10^n / 2 m runden
const double n = pow(10, floor(log10(length))) / 2;
m_length = lrint(ceil(length / n) * n);
m_scaleX = pow(10, floor(log10(length)) - 1);
if (m_scaleX < 0.1)
m_scaleX = 0.1;
m_nullX = 0;
double len = 0;
m_drawPoints.moveTo(len, m_data.at(0).second);
m_heightList.append(QPair<double, double>(len, m_data.at(0).second));
for (int i = 1; i < m_data.count(); ++i)
{
len += getLength(m_data.at(i - 1).first, m_data.at(i).first);
m_drawPoints.lineTo(len, m_data.at(i).second);
m_heightList.append(QPair<double, double>(len, m_data.at(i).second));
//fprintf(stderr, "point at len %f\n", len);
}
m_drawPoints.lineTo(len, m_minElevation);
m_drawPoints.lineTo(0, m_minElevation);
m_drawPoints.setFillRule(Qt::WindingFill);
}
void LtsHeightPlotWidget::Private::setData(const QVector< QPair< QPointF, double > > &data)
{
if (data.isEmpty())
{
clear();
return;
}
QSize tmp = m_curSize;
clear();
m_data = data;
recalcData();
resize(tmp);
}
void LtsHeightPlotWidget::Private::setMarks(const QVector< QPointF > &data)
{
m_marks = data;
recalcData();
repaint();
}
void LtsHeightPlotWidget::Private::resize(const QSize &newSize)
{
if (newSize == m_curSize)
return;
m_curSize = newSize;
const int border = 25;
const double width = m_curSize.width();
const double height = m_curSize.height();
const double minY = m_minElevation - m_scaleY;
const double maxY = m_maxElevation + m_scaleY;
const double wertebereichY = maxY - minY;
const double minX = 0 - m_scaleX;
const double maxX = m_length + m_scaleX;
const double wertebereichX = maxX - minX;
m_trans1.reset();
m_trans2.reset();
// 10 Pixel nach rechts und y = 0 ist unten anstatt oben
m_trans1.translate(10, height);
// + und - y tauschen - damit im ersten Quadrant x und y positiv sind
m_trans1.scale(1, -1);
// und noch etwas vom Rand weg
m_trans1.translate(border, border);
// und wieder zurueck
m_trans1.scale(1, -1);
// die aktuelle Breite und Hoehe relativ zu der Breite und Hoehe der Daten
m_trans2.scale((width - 2 * border) / (wertebereichX),
(height - 2 * border) / (wertebereichY));
// m_minElevation ist unser '0-Wert', minY demnach der kleinste y-Wert
m_trans2.translate(0, minY);
// + und - y tauschen - damit im ersten Quadrant x und y positiv sind
m_trans2.scale(1, -1);
m_transInverted = m_trans1.inverted() * m_trans2.inverted();
m_transCombined = m_trans2 * m_trans1;
repaint();
}
void LtsHeightPlotWidget::Private::repaint()
{
if (!m_curSize.isValid())
return;
if (!m_heightList.count())
return;
QImage img(m_curSize, QImage::Format_ARGB32);
img.fill(Qt::transparent);
QPainter p(&img);
const QPainter::RenderHints hints = QPainter::HighQualityAntialiasing |
QPainter::SmoothPixmapTransform |
QPainter::TextAntialiasing |
QPainter::Antialiasing;
p.setRenderHints(hints);
p.setTransform(m_trans1);
// Daten zeichnen
p.setPen(QPen(m_fillBrush, 0.1));
p.setBrush(m_fillBrush);
p.drawPath(m_trans2.map(m_drawPoints));
p.setRenderHint(QPainter::Antialiasing, false);
// Marker zeichnen
p.setPen(m_markerPen);
for (int i = 0; i < m_marks.count(); ++i)
{
int posIdx = findDataPointIdx(m_marks.at(i));
if (posIdx < 0)
continue;
double len = getLength(m_data.at(posIdx).first, m_marks.at(i));
len += m_heightList.at(posIdx).first;
const double height = getDataHeight(len);
const QLineF l(len, m_minElevation, len, height);
//fprintf(stderr, "marker at len %f\n", len);
p.drawLine(m_trans2.map(l));
}
// Koordinatensystem + Beschriftung
p.setPen(m_axisPen);
p.setFont(m_textFont);
// Y-Achse
p.drawLine(m_trans2.map(QPointF(m_nullX, m_minElevation - m_scaleY)),
m_trans2.map(QPointF(m_nullX, m_maxElevation + m_scaleY)));
// kleine Striche
for (double i = m_minElevation; i <= m_maxElevation; i += m_scaleY)
{
const qreal y = m_trans2.map(QPointF(0, i)).y();
p.drawLine(QLineF(-2, y, 2, y));
}
// grosse Striche
for (double i = m_minElevation; i <= m_maxElevation; i += m_scaleY * 5)
{
const qreal y = m_trans2.map(QPointF(0, i)).y();
p.drawLine(QLineF(-4, y, 4, y));
}
QFontMetricsF fm(m_textFont);
for (double i = m_minElevation; i <= m_maxElevation; i += m_scaleY * 5)
{
if (qFuzzyCompare(i, m_nullY))
continue;
const qreal y = m_trans2.map(QPointF(m_nullX, i)).y();
const QPointF p1(-20, y);
const QString str = QString::number(i);
QRectF r = fm.boundingRect(str);
r.setWidth(fm.width(str)); // important, see docu!
r.moveTo(p1.x() - r.width() / 2., p1.y() - r.height() / 2.);
p.drawText(r, str, Qt::AlignHCenter | Qt::AlignVCenter);
}
// X-Achse
p.drawLine(m_trans2.map(QPointF(-m_scaleX, m_nullY)),
m_trans2.map(QPointF(m_scaleX + m_length, m_nullY)));
// kleine Striche
for (double i = m_scaleX; i <= m_length; i += m_scaleX)
{
const QPointF point = m_trans2.map(QPointF(i, m_nullY));
p.drawLine(QLineF(point.x(), point.y() - 2, point.x(), point.y() + 2));
}
// grosse Striche
for (double i = 0; i <= m_length; i += m_scaleX * 5)
{
const QPointF point = m_trans2.map(QPointF(i, m_nullY));
p.drawLine(QLineF(point.x(), point.y() - 4, point.x(), point.y() + 4));
}
for (double i = 0; i <= m_length; i += m_scaleX * 5)
{
if (qFuzzyCompare(i, m_nullX))
continue;
const QPointF point = m_trans2.map(QPointF(i, m_nullY));
const QPointF p1(point.x(), point.y() + 15);
const QString str = QString::number(i);
QRectF r = fm.boundingRect(str);
r.setWidth(fm.width(str)); // important, see docu!
r.moveTo(p1.x() - r.width() / 2., p1.y() - r.height() / 2.);
p.drawText(r, str, Qt::AlignHCenter | Qt::AlignVCenter);
}
p.end();
m_pixmap = QPixmap::fromImage(img);
}
void LtsHeightPlotWidget::Private::paintLines(QPainter &p) const
{
if (m_heightList.count() > 0)
{
// Mouse-Position zeichnen
QPointF pos = m_mousePos;
if (pos.x() < 0)
pos.setX(0);
if (pos.x() > m_heightList.at(m_heightList.count() - 1).first)
pos.setX(m_heightList.at(m_heightList.count() - 1).first);
//keine Transformation setzen da sonst die Linienstaerke des Pens veraendert wird!
//p.setTransform(d->m_transCombined);
const double curHeight = getDataHeight(pos.x());
p.setPen(m_mousePosPen);
p.drawLine(m_transCombined.map(QLineF(m_nullX, curHeight, pos.x(), curHeight)));
p.drawLine(m_transCombined.map(QLineF(pos.x(), m_nullY, pos.x(), curHeight)));
// Sichtlinie zeichnen
if (!m_mouseLeftClickPos.isNull())
{
const double y = curHeight > pos.y() ? curHeight : pos.y();
p.drawLine(m_transCombined.map(QLineF(m_nullX, y, pos.x(), y)));
p.drawLine(m_transCombined.map(QLineF(pos.x(), m_nullY, pos.x(), y)));
p.setPen(m_viewLinePen);
const QPointF &c = m_mouseLeftClickPos;
p.drawLine(m_transCombined.map(QLineF(m_nullX, c.y(), c.x(), c.y())));
p.drawLine(m_transCombined.map(QLineF(c.x(), m_nullY, c.x(), c.y())));
// nur hier da die anderen beiden Linien sonst nicht schoen aussehen
const QLineF line(m_mouseLeftClickPos, QPointF(pos.x(), y));
p.drawLine(m_transCombined.map(line));
}
}
}
LtsHeightPlotWidget::LtsHeightPlotWidget(QWidget *parent)
: QWidget(parent)
, d(new Private)
{
setMinimumSize(300, 300);
resize(size());
setMouseTracking(true);
}
LtsHeightPlotWidget::~LtsHeightPlotWidget()
{
delete d;
}
void LtsHeightPlotWidget::clearView()
{
d->clearPixmap();
update();
}
void LtsHeightPlotWidget::setData(const QVector< QPair< QPointF, double > > &data)
{
d->setData(data);
update();
}
void LtsHeightPlotWidget::setMarks(const QVector<QPointF> &data)
{
d->setMarks(data);
}
void LtsHeightPlotWidget::setBackgroundBrush(const QBrush &brush)
{
QPalette pal = palette();
pal.setBrush(QPalette::Background, brush);
setPalette(pal);
}
void LtsHeightPlotWidget::setTextFont(const QFont &f)
{
d->m_textFont = f;
d->repaint();
}
void LtsHeightPlotWidget::setAxisPen(const QPen &pen)
{
d->m_axisPen = pen;
d->repaint();
}
void LtsHeightPlotWidget::setPositionPen(const QPen &pen)
{
d->m_mousePosPen = pen;
}
void LtsHeightPlotWidget::setViewLinePen(const QPen &pen)
{
d->m_viewLinePen = pen;
}
void LtsHeightPlotWidget::setContourBrush(const QBrush &brush)
{
d->m_fillBrush = brush;
d->repaint();
}
void LtsHeightPlotWidget::setMarkerPen(const QPen &pen)
{
d->m_markerPen = pen;
d->repaint();
}
void LtsHeightPlotWidget::resizeEvent(QResizeEvent *e)
{
d->resize(size());
QWidget::resizeEvent(e);
}
void LtsHeightPlotWidget::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
QPainter p(this);
const QPainter::RenderHints hints = QPainter::SmoothPixmapTransform |
QPainter::TextAntialiasing |
QPainter::Antialiasing;
p.setRenderHints(hints);
// vorgefertiges Bild zeichnen
p.drawPixmap(0, 0, d->m_pixmap);
// und jetzt die Hilfslinien
d->paintLines(p);
p.end();
}
void LtsHeightPlotWidget::mouseMoveEvent(QMouseEvent *e)
{
QWidget::mouseMoveEvent(e);
if (!d->m_heightList.count())
return;
const QPointF pos = d->m_transInverted.map(e->localPos());
emit mousePositionChanged(d->getDataPos(pos.x()), d->getDataHeight(pos.x()), pos.y());
d->m_mousePos = pos;
update();
}
void LtsHeightPlotWidget::mouseReleaseEvent(QMouseEvent *e)
{
QWidget::mouseReleaseEvent(e);
if (!d->m_heightList.count())
return;
if (e->button() == Qt::LeftButton)
{
const QPointF &pos = d->m_mousePos;
if (pos.x() < 0)
return;
if (pos.x() > d->m_heightList.at(d->m_heightList.count() - 1).first)
return;
const double curHeight = d->getDataHeight(pos.x());
d->m_mouseLeftClickPos = QPointF(pos.x(), curHeight > pos.y() ? curHeight : pos.y());
emit startLineOfSight(d->getDataPos(pos.x()));
}
else if (e->button() == Qt::RightButton)
{
d->m_mouseLeftClickPos = QPointF();
emit stopLineOfSight();
update();
}
}
+97
View File
@@ -0,0 +1,97 @@
/**
* @RDE_HEADER@
* Widget that visualizes the topography of a bunch of points along a rubberband
* $Id$
* $HeadURL$
* $Author$
* Created by Malte Schroeder, NLSE, 2018-03-07
*/
#pragma once
#include <QWidget>
#include <QtCore/QVector>
#include <QtCore/QPointF>
#include <QtCore/QPair>
#include "lts_widgets_export.h"
class LTSWIDGETS_EXPORT LtsHeightPlotWidget final : public QWidget
{
Q_OBJECT
public:
explicit LtsHeightPlotWidget(QWidget *parent = nullptr);
virtual ~LtsHeightPlotWidget();
void clearView();
/**
* Set the data in the form
* QPoint - x/y values
* double - height values
* @param data the height data to display
*/
void setData(const QVector<QPair<QPointF, double> > &data);
/**
* Set the markers to show
* QPoint - x/y values
* @param data the markers to display
*/
void setMarks(const QVector<QPointF> &data);
/**
* Set the background of the widget
*/
void setBackgroundBrush(const QBrush &brush);
/**
* Set the font for the axis labels
*/
void setTextFont(const QFont &f);
/**
* Set the pen used for the axis lines
*/
void setAxisPen(const QPen &pen);
/**
* Set the pen for the position marker
*/
void setPositionPen(const QPen &pen);
/**
* Set the pen for the viewline
*/
void setViewLinePen(const QPen &pen);
/**
* Set the brush for the contour (+ fill)
*/
void setContourBrush(const QBrush &brush);
/**
* Set the pen for the markers
*/
void setMarkerPen(const QPen &pen);
Q_SIGNALS:
/**
* The current mouse position has changed.
* @param curPoint the current point (in map coordinates) of the mouse
* @param curHeight the current height at the given point
* @param mouseHeight the current height at the current mouse point (>= curHeight)
*/
void mousePositionChanged(const QPointF &curPoint, double curHeight, double mouseHeight);
/**
* A line of sight has started. This signal can emitted more than once
* wich means that the old line of sight was stopped
* @param startPoint the startpoint (in map coordinates) of the line
*/
void startLineOfSight(const QPointF &startPoint);
/**
* A line of sight has stopped
*/
void stopLineOfSight();
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
class Private;
Private *const d;
};
+123
View File
@@ -0,0 +1,123 @@
/**
* @RDE_HEADER@
* An Image Editor which shows an Image, and emits a signal when clicked (to edit/swap an Image)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-09-19
*/
#include "lts_image_editor.h"
#include <QPainter>
#include <QPaintEvent>
#include <QBrush>
#include <QPen>
#include <QRect>
#include <QEvent>
using namespace LtsWidgets;
LtsImageEditor::LtsImageEditor(QWidget *parent)
: QWidget(parent)
, m_isHovered(false)
, m_menuEnabled(true) // enabled by default
, m_bDrawOuterRect(true)
{}
void LtsImageEditor::setPixmap(const QPixmap &pixmap)
{
if (pixmap.isNull()) // invalid / empty pixmap given, trying to replace it with one from lts_resources
m_pixmap = QPixmap(QStringLiteral(":style/noimage.png")); // when this fails, the pixmap is still invalid, so no checks needed TODO: add noimage.png !
else
m_pixmap = pixmap;
update();
emit imageChanged();
}
void LtsImageEditor::enterEvent(QEvent *)
{
//qWarning() << "enter";
m_isHovered = true;
update();
}
void LtsImageEditor::leaveEvent(QEvent *)
{
//qWarning() << "leave";
m_isHovered = false;
update();
}
bool LtsImageEditor::event(QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress && m_menuEnabled)
{
emit signalClicked();
event->accept();
m_isHovered = false; // unhovered on click
update();
return true;
}
return QWidget::event(event); // otherwise..
}
void LtsImageEditor::paintEvent(QPaintEvent* event) // TODO: colors from palette (style plugin) ?
{
QPainter p(this);
// draw profile picture
p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
QBrush brush = QColor(QStringLiteral("white")); // TODO: get from palette
if (!m_pixmap.isNull())
{
if (!(m_isHovered && m_menuEnabled)) // draw if NOT hovered or draw when hovered but menu is disabled
{
const QSize s(size());
const QPixmap pixmap(m_pixmap.scaled(s, Qt::KeepAspectRatio, Qt::SmoothTransformation));
const int x = qRound((s.width() - pixmap.width()) / 2.);
const int y = qRound((s.height() - pixmap.height()) / 2.);
if (m_bDrawOuterRect)
{
p.setPen(QPen(QColor("gray"), 2));
p.drawRect(rect());
}
p.drawPixmap(x, y, pixmap);
event->accept();
return;
}
}
// draw frame
p.setPen(QPen(QColor("gray"), 2));
p.setBrush(brush);
p.drawRect(rect());
// draw menu icon
if (m_isHovered && m_menuEnabled)
{
p.setPen(QPen(QColor(QStringLiteral("gray")), (size().height() / 48 * 2))); // respect height, width is unnecessary
double xc = size().width()/2.012;
double yc = size().height()/2.012;
p.drawLine(QPointF(xc - (xc/2.01f), yc - (yc/3.01f)), QPointF(xc + (xc/2.02f), yc - (yc/3.02f)));
p.drawLine(QPointF(xc - (xc/2.01f), yc - 0.01f), QPointF(xc + (xc/2.02f), yc - 0));
p.drawLine(QPointF(xc - (xc/2.01f), yc + (yc/3.01f)), QPointF(xc + (xc/2.02f), yc + (yc/3.02f)));
}
event->accept();
}
+74
View File
@@ -0,0 +1,74 @@
/**
* @RDE_HEADER@
* An Image Editor which shows an Image, and emits a signal when clicked (to edit/swap an Image)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-09-19
*/
#pragma once
#include "lts_widgets_export.h"
#include <QWidget>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsImageEditor : public QWidget
{
Q_OBJECT
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
Q_PROPERTY(bool menuEnabled READ isMenuEnabled WRITE setMenuEnabled)
public:
explicit LtsImageEditor(QWidget *parent = nullptr);
/**
* @brief setPixmap sets the picture of this button. If no pixmap / picture is set, the picture-layer is button colored.
* @param pixmap The picture to draw on the button.
*/
void setPixmap(const QPixmap &pixmap);
/**
* @brief getPixmap TODO
* @return
*/
QPixmap pixmap() const { return m_pixmap; }
/**
* @brief setMenuEnabled changes the hover & click behaviour. If the menu is enabled, the profile button indicates a menu on hovering, and by clicking it, the signal 'signalClicked' is emitted (menu is enabled by default)
* @param enabled true if menu is enabled, false if not
*/
void setMenuEnabled(bool enabled = true) { m_menuEnabled = enabled; }
bool isMenuEnabled() const { return m_menuEnabled; }
void setDrawOuterRect(bool bDraw) { m_bDrawOuterRect = bDraw; }
bool drawOuterRect() const { return m_bDrawOuterRect; }
protected:
void paintEvent(QPaintEvent *event);
void enterEvent(QEvent *);
void leaveEvent(QEvent *);
bool event(QEvent *event);
private:
QPixmap m_pixmap;
bool m_isHovered;
bool m_menuEnabled;
bool m_bDrawOuterRect;
signals:
/**
* @brief signalClicked is emitted when this widget has been clicked.
*/
void signalClicked();
void imageChanged();
};
}
+88
View File
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* Info Dialog, containing informations about this software & build
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, NLSE, 2018-02-26
*/
#include "lts_info_dialog.h"
#include "ui_lts_info_dialog.h"
#include <QPixmap>
#include <QFile>
#include <QDebug>
#ifndef LTS_VERSION_MAJOR
#define LTS_VERSION_MAJOR "undefined"
#endif
#ifndef LTS_VERSION_MINOR
#define LTS_VERSION_MINOR "undefined"
#endif
#ifndef LTS_VERSION_PATCH
#define LTS_VERSION_PATCH "undefined"
#endif
#ifndef LTS_VERSION_DATE
#define LTS_VERSION_DATE "undefined"
#endif
#ifndef JENKINS_BUILD_NUMBER
#define JENKINS_BUILD_NUMBER "unknown"
#endif
static const char ver_major[] = LTS_VERSION_MAJOR;
static const char ver_minor[] = LTS_VERSION_MINOR;
static const char ver_patch[] = LTS_VERSION_PATCH;
static const char ver_date[] = LTS_VERSION_DATE;
static const char ver_jenkins[] = JENKINS_BUILD_NUMBER;
namespace LtsWidgets
{
LtsInfoDialog::LtsInfoDialog(QWidget *parent)
: LtsFramelessDialog(parent)
, m_ui(new Ui::LtsInfoDialog)
{
m_ui->setupUi(this);
QFont f = font();
f.setBold(true);
m_ui->lblVersion->setFont(f);
m_ui->lblDate->setFont(f);
const QString versionString = QStringLiteral("%1.%2-%3 (rev: %4)")
.arg(QString::fromLatin1(ver_major),
QString::fromLatin1(ver_minor),
QString::fromLatin1(ver_jenkins),
QString::fromLatin1(ver_patch));
m_ui->lblVersion->setText(versionString);
m_ui->lblDate->setText(QString::fromLatin1(ver_date));
// project logo (have different sizes)
QString projectLogoFilename(QStringLiteral(":project/login_logo.png"));
if (!QFile(projectLogoFilename).exists())
projectLogoFilename = QStringLiteral(":/project/logo.png");
QPixmap pixmap(projectLogoFilename);
if (pixmap.isNull())
{
qWarning() << "LtsInfoDialog: project logo missing or is not an valid image " << projectLogoFilename;
}
else
{
int max = m_ui->lblProjectLogo->maximumHeight();
pixmap = pixmap.scaled(999, max * 0.85f, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_ui->lblProjectLogo->setPixmap(pixmap);
}
connect(m_ui->pbClose, &QPushButton::clicked, this, &LtsInfoDialog::close);
}
LtsInfoDialog::~LtsInfoDialog()
{
delete m_ui;
}
} // namespace LtsWidgets
+35
View File
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Info Dialog, containing informations about this software & build
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, NLSE, 2018-02-26
*/
#pragma once
#include "lts_frameless_dialog.h"
#include "lts_widgets_export.h"
namespace Ui
{
class LtsInfoDialog;
}
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsInfoDialog : public LtsFramelessDialog
{
Q_OBJECT
public:
explicit LtsInfoDialog(QWidget *parent = nullptr);
~LtsInfoDialog();
private:
Ui::LtsInfoDialog *m_ui;
};
} // namespace LtsWidgets
+135
View File
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsInfoDialog</class>
<widget class="QWidget" name="LtsInfoDialog">
<property name="windowTitle">
<string>Operator Menu</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label">
<property name="text">
<string>Rheinmetall Electronics GmbH</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>450</width>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>450</width>
<height>200</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>:/rde_komplett.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblProjectLogo">
<property name="minimumSize">
<size>
<width>0</width>
<height>150</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="text">
<string notr="true">&lt;Project Logo&gt;</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Version:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblVersion">
<property name="text">
<string notr="true">?</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblDate">
<property name="text">
<string notr="true">?</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item alignment="Qt::AlignRight">
<widget class="QPushButton" name="pbClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+154
View File
@@ -0,0 +1,154 @@
/**
* @RDE_HEADER@
* Class of Led Widget
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_led_widget.h"
#include <QPainter>
#include <QPropertyAnimation>
#include <QResizeEvent>
namespace LtsWidgets
{
void LtsLedWidget::resizeEvent(QResizeEvent *event)
{
switch (m_aspectHint) {
case AspectHint::NoHint:
{
// NOTE: the next 4 lines would overwrite set properties from ui files
// setMinimumWidth(0);
// setMaximumWidth(65000);
// setMinimumHeight(0);
// setMaximumHeight(65000);
QWidget::resizeEvent(event);
break;
}
case AspectHint::HeightIsWidth:
setMinimumHeight(event->size().width());
setMaximumHeight(event->size().width());
setMinimumWidth(0);
setMaximumWidth(65000);
break;
case AspectHint::WidthIsHeight:
setMinimumHeight(0);
setMaximumHeight(65000);
setMinimumWidth(event->size().height());
setMaximumWidth(event->size().height());
break;
}
}
/**
* The following values were determined by trial and error:
*/
static constexpr double GRADIENT_KEY_LOWER = 0.3;
static constexpr double GRADIENT_KEY_UPPER = 0.9;
static constexpr int GRADIENT_VALUE_LOWER = 125;
static constexpr int GRADIENT_VALUE_UPPER = 165;
static constexpr double GRADIENT_CENTER_OFFSET = 5.0;
static constexpr double GRADIENT_FOCALPOINT_OFFSET = 2.7;
static constexpr double PERCENTAGE_OF_BORDER = 0.1;
static constexpr int DARK_FACTOR = 200;
LtsLedWidget::LtsLedWidget(QWidget *parent, const QColor &oncolor, const QColor &offcolor, bool useGradient, int animtime)
: QWidget(parent)
, m_status(true)
, m_oncolor(oncolor)
, m_offcolor(offcolor)
, m_currentcolor(oncolor)
, m_gradient(useGradient)
, m_animTime(animtime)
{
setObjectName(QString::fromUtf8("LtsLedWidget"));
}
void LtsLedWidget::paintEvent(QPaintEvent *pe)
{
Q_UNUSED(pe);
QPainter p;
p.begin(this);
p.setOpacity(QWidget::isEnabled() ? 1.00 : 0.5);
int minSize = height() > width() ? width() : height();
if (m_gradient)
{
QRadialGradient grad(QPointF(width(), height()) / GRADIENT_CENTER_OFFSET, // Center
minSize, // Radius
QPointF(width(), height()) / GRADIENT_FOCALPOINT_OFFSET); // FocalPoint
grad.setColorAt(GRADIENT_KEY_LOWER, m_currentcolor.lighter(GRADIENT_VALUE_LOWER));
grad.setColorAt(GRADIENT_KEY_UPPER, m_currentcolor.darker(GRADIENT_VALUE_UPPER));
p.setBrush(grad);
}
else
p.setBrush(QBrush(m_currentcolor, Qt::SolidPattern));
p.setRenderHint(QPainter::Antialiasing);
p.setPen(QPen(Qt::black, PERCENTAGE_OF_BORDER * minSize, Qt::SolidLine, Qt::SquareCap));
p.drawEllipse(QPointF(width(), height()) / 2.0,
width() / (2.0 * (1.00 + PERCENTAGE_OF_BORDER)),
height() / (2.0 * (1.00 + PERCENTAGE_OF_BORDER)));
p.end();
}
void LtsLedWidget::animate()
{
QPropertyAnimation *anim = new QPropertyAnimation(this, "currentcolor");
anim->setDuration(m_animTime);
anim->setEasingCurve(QEasingCurve::OutBack);
anim->setStartValue(m_currentcolor);
anim->setEndValue(m_status ? m_oncolor : m_offcolor == Qt::black ? m_oncolor.darker(DARK_FACTOR) : m_offcolor);
anim->start(QPropertyAnimation::DeleteWhenStopped);
}
void LtsLedWidget::setStatus(bool status)
{
if (m_status == status)
return;
m_status = status;
animate();
}
void LtsLedWidget::setUseGradient(bool useGradient)
{
if (m_gradient == useGradient)
return;
m_gradient = useGradient;
repaint();
}
void LtsLedWidget::setColor(const QColor &oncolor, const QColor &offcolor)
{
setOnColor(oncolor);
setOffColor(offcolor);
}
void LtsLedWidget::setOnColor(const QColor &oncolor)
{
if (m_oncolor == oncolor)
return;
m_oncolor = oncolor;
if (m_status)
animate();
}
void LtsLedWidget::setOffColor(const QColor &offcolor)
{
if (m_offcolor == offcolor)
return;
m_offcolor = offcolor;
if (!m_status)
animate();
}
void LtsLedWidget::setCurrentColor(const QColor &currentcolor)
{
if (m_currentcolor == currentcolor)
return;
m_currentcolor = currentcolor;
repaint();
}
} // namespace namespace LtsWidgets
+178
View File
@@ -0,0 +1,178 @@
/**
* @RDE_HEADER@
* LTS LED Widget
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QWidget>
#include "lts_widgets_export.h"
namespace LtsWidgets
{
/**
* This class generates a LED with 2D/3D design, customizable colors and animation.
*
* If you need more than two states, simple derive from this class, only use "status=on"
* generate pairs of identifiers and colors and change the colors with appropriate method:
* e.g.:
* DerivedLEDWidget(...) : LtsLedWidget() {}
* enum Status { ST_STATUS1 = 0, ST_STATUS2, ST_STATUS3 };
* static QMap<Status, QColor> cMap; // Fill it respectively
* DerivedLEDWidget::setStatus(Status b) { setOnColor(cMap.value(b, Qt::black)); }
*/
class LTSWIDGETS_EXPORT LtsLedWidget : public QWidget
{
Q_OBJECT
public:
enum class AspectHint
{
WidthIsHeight,
NoHint,
HeightIsWidth
};
Q_ENUM(AspectHint)
Q_PROPERTY(QColor oncolor READ onColor WRITE setOnColor)
Q_PROPERTY(QColor offcolor READ offColor WRITE setOffColor)
Q_PROPERTY(bool status READ status WRITE setStatus)
Q_PROPERTY(bool usesGradient READ usesGradient WRITE setUseGradient)
Q_PROPERTY(int animTime READ animTime WRITE setAnimTime)
Q_PROPERTY(AspectHint aspectHint READ aspectHint WRITE setAspectHint)
Q_PROPERTY(QColor currentcolor READ currentColor WRITE setCurrentColor DESIGNABLE false)
/**
* Constructor
* @param parent The widget's parent. Default value is NULL.
* @param oncolor The color for LED when turned on. Default value is Qt::red, see also setOnColor() for details.
* @param offcolor The color for LED when turned off. Default value is Qt::black (i.e. "Use darker color"), see also setOffColor() for details.
* @param useGradient The 2D/3D design status. Default value is true (3D).
* @param animtime The animation time in msecs. Default value is 0.
*/
explicit LtsLedWidget(QWidget *parent = nullptr, const QColor &oncolor = Qt::red, const QColor &offcolor = Qt::black, bool useGradient = true, int animtime = 0);
void paintEvent(QPaintEvent *pe);
/**
* Set animation time in msec
* Value = 0 shows no animation
* Reasonable value is between 400 and 700 msecs
* @param time time in msecs
*/
void setAnimTime(int time) { m_animTime = time; }
/**
* Get current animation time in msecs
* @return animation time in msecs
*/
int animTime() const { return m_animTime; }
/**
* Get current led status
* @return the LED's status
*/
bool status() const { return m_status; }
/**
* Set 2D/3D design
* Value true set a 3D design of the led widget,
* Value false set a 2D design of the led widget
* @param useGradient
*/
void setUseGradient(bool useGradient);
/**
* Get a state info about the current 2D/3D design
* @return true for 3D and false for 2D design
*/
bool usesGradient() const { return m_gradient; }
/**
* Set the color for the LED when turned "on".
* If status is "on" and the oncolor is changed
* the animation will be run.
* @param oncolor color for LED in state "on"
*/
void setOnColor(const QColor &oncolor);
/**
* Get the current oncolor
* @return the current oncolor
*/
QColor onColor() const { return m_oncolor; }
/**
* Set the color for the LED when turned "off".
* If offcolor is black the wigdet use a darker version of oncolor as offcolor value.
* If status is "off" and the offcolor is changed
* the animation will be run.
* @param offcolor color for LED in state "off"
*/
void setOffColor(const QColor &offcolor);
/**
* Get the current offcolor
* @return the current offcolor
*/
QColor offColor() const { return m_offcolor; }
/**
* Convenience function set oncolor and offcolor in one step
* Default value for offcolor is black.
* If offcolor is black the wigdet use a darker version of oncolor as offcolor value.
* @param oncolor The color for LED when turned on.
* @param offcolor The color for LED when turned off. Default value is Qt::black (i.e. "Use darker color").
*/
void setColor(const QColor &oncolor, const QColor &offcolor = Qt::black);
/**
* Get current color
* @return the currently visible color
*/
QColor currentColor() const { return m_currentcolor; }
void resizeEvent(QResizeEvent *event);
AspectHint aspectHint() const { return m_aspectHint; }
public slots:
/**
* Public slot set current led status
* @param status The LED's status (on/off)
*/
void setStatus(bool status);
void setAspectHint(AspectHint aspectHint)
{
m_aspectHint = aspectHint;
}
private:
/**
* Set current color
* Needed for color animation
* ***This method is private for a reason!***
* @param currentcolor
*/
void setCurrentColor(const QColor &currentcolor);
/**
* Private function to start the color animation
*/
void animate();
bool m_status;
QColor m_oncolor;
QColor m_offcolor;
QColor m_currentcolor;
bool m_gradient;
int m_animTime;
AspectHint m_aspectHint = AspectHint::NoHint;
};
} // namespace LtsWidgets
+133
View File
@@ -0,0 +1,133 @@
#include "lts_message_dialog.h"
#include "lts_message_dialog_helper.h"
#include <QCoreApplication>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QDebug>
namespace LtsWidgets
{
namespace LtsMessageDialog
{
Button showMessage(QWidget* parent,
const QString& message,
const QString &title,
const Behaviour behaviour,
const Buttons buttons,
const QHash<Button, QString> &buttonTexts)
{
static QAtomicInt s_used = 0;
if (s_used)
qDebug() << "LtsMessageDialog: showMessage called while showMessage is in its Event Loop - do not open more than one Dialog at once !!";
s_used = 1;
LtsMessageDialogHelper *dialog = new LtsMessageDialogHelper(); // NOT 'parent' !
Button result = dialog->showMessage(parent, message, title, behaviour, buttons, buttonTexts);
dialog->deleteLater();
s_used = 0;
return result;
}
Button askApplyDiscardCancel(QWidget *parent)
{
const QString question = QCoreApplication::translate("LtsDialog",
"<br>"
"Apply - apply changes and close.<br>"
"Discard - discard changes and close.<br>"
"Cancel - do not close.");
const QString title = QCoreApplication::translate("LtsDialog", "Unapplied changes made. Apply the changes?");
return LtsMessageDialog::showMessage(parent,
question,
title,
Behaviour::Modal,
Button::Apply | Button::Discard | Button::Cancel);
}
} // namespace LtsMessageDialog
// LtsWidgets
LtsMessageDialogNew::LtsMessageDialogNew(QWidget *parent, const QString &message, const QString &title, const Behaviour behaviour, const Buttons buttons, const QHash<Button, QString> &buttonTexts, bool deleteOnClose)
{
m_dialog = new LtsFramelessDialog(parent, behaviour);
if (deleteOnClose)
m_dialog->setAttribute(Qt::WA_DeleteOnClose); // Message Dialogs are loose instances that must delete theirselves, because noone keeps a pointer to the message dialog
connect(m_dialog, &LtsFramelessDialog::destroyed,
this, &LtsMessageDialogNew::deleteLater);
QVBoxLayout *mainLayout = new QVBoxLayout(m_dialog);
QString text;
if (!title.isEmpty()) // title given?
{
text = QStringLiteral("<b>%1</b><br><br>").arg(title);
}
text += message;
QLabel *messageLabel = new QLabel(text, m_dialog);
messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); // make text selectable by mouse
mainLayout->addWidget(messageLabel);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->setMargin(0);
// TODO: FIXME: can this be called after the selection was made and the shadow just dies ?
//connect(m_dialog, &LtsFramelessDialog::rejected, this, &LtsMessageDialogHelper::slotCancel); // respect that non-modal dialogs can be closed by clicking outside of it
if (buttons.testFlag(Button::Ok))
connect(addButton(buttonTexts.value(Button::Ok, tr("Ok")), buttonLayout, m_dialog, Button::Ok, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::ok);
if (buttons.testFlag(Button::Yes))
connect(addButton(buttonTexts.value(Button::Yes, tr("Yes")), buttonLayout, m_dialog, Button::Yes, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::yes);
if (buttons.testFlag(Button::No))
connect(addButton(buttonTexts.value(Button::No, tr("No")), buttonLayout, m_dialog, Button::No, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::no);
if (buttons.testFlag(Button::Apply))
connect(addButton(buttonTexts.value(Button::Apply, tr("Apply")), buttonLayout, m_dialog, Button::Apply, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::apply); // same as yes
if (buttons.testFlag(Button::Discard))
connect(addButton(buttonTexts.value(Button::Discard, tr("Discard")), buttonLayout, m_dialog, Button::Discard, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::discard); // same as no
if (buttons.testFlag(Button::Cancel))
connect(addButton(buttonTexts.value(Button::Cancel, tr("Cancel")), buttonLayout, m_dialog, Button::Cancel, parent), &QPushButton::clicked, this, &LtsMessageDialogNew::cancel);
QWidget *buttonLayoutWidget = new QWidget;
buttonLayoutWidget->setLayout(buttonLayout);
mainLayout->addWidget(buttonLayoutWidget);
m_dialog->show();
m_dialog->raise();
}
QPushButton* LtsMessageDialogNew::addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent)
{
QPushButton* btn = new QPushButton(text, parent);
connect(btn, &QPushButton::clicked, dialog, &LtsFramelessDialog::accept);
thatLayout->addWidget(btn);
if (button == Button::Ok ||
button == Button::Cancel) // ok and cancel should not be used together, so there is no check needed (as only one button can be the default button)
{
btn->setDefault(true);
}
return btn;
}
} // namespace LtsWidgets
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include "lts_widgets_export.h"
#include "lts_frameless_dialog.h"
#include <QString>
#include <QHash>
class QWidget;
class QHBoxLayout;
namespace LtsWidgets
{
/**
* @brief The Button enum describes one or more buttons, which should be shown on a Dialog which supports LtsWidgets::Button. Also this enum
* is the return type of LtsMessageDialog and provides the button clicked to close the Popup-Dialog.
*/
enum class Button
{
UNKNOWN = 0,
Ok = 1,
Yes = 2,
No = 4,
Cancel = 8,
Apply = 16,
Discard = 32,
};
Q_DECLARE_FLAGS(Buttons, Button)
inline uint qHash(LtsWidgets::Button b)
{
return ::qHash(static_cast<int>(b));
}
/**
* @brief [Please consider using LtsMessageDialogNew] The LtsMessageDialog class provides a BLOCKING Popup-Dialog (based on LtsFramelessDialog) to show a message to the user with selectable buttons. This class is static and cannot be instantiated.
* To show a message to the user class 'LtsMessageDialog::showMessage(..)'. And to get the button that was selected by the user to close the dialog, consider the return type 'LtsWidgets::Button'.
* This Popup-Dialog closes automatically if any button on it is clicked.
* The return type is an enum class instance which contains the information about the clicked button.
* If no button was specified there is no chance to close the dialog if LtsWidgets::Behaviour::Modal was specified.
*/
namespace LtsMessageDialog // in reality this is the helper, but for "naming's sake", this is not called the helper
{
/**
* @brief showMessage shows a Popup-Dialog to show a message to the user with selectable buttons - method is blocking (creates new EventLoop)!
* @param parent The widget to be covered.
* @param message The message to the user.
* @param behaviour How the Popup-Dialog should behave (see LtsFramelessDialog).
* @param buttons A list of Buttons to provide to the user.
* @param buttonTexts If set, the button texts are used from there instead the default ones
* @return The button that has been clicked by the user to close this Popup-Dialog.
*/
LTSWIDGETS_EXPORT Button showMessage(QWidget* parent,
const QString &message,
const QString &title = QString(),
const Behaviour behaviour = Behaviour::Modal,
const Buttons buttons = Button::Ok,
const QHash<Button, QString> &buttonTexts = QHash<Button, QString>());
LTSWIDGETS_EXPORT Button askApplyDiscardCancel(QWidget *parent);
} // namespace LtsMessageDialog
/**
* @brief The LtsMessageDialog class provides a NON-BLOCKING Popup-Dialog (based on LtsFramelessDialog) to show a message to the user with selectable buttons.
* To show a message to the user use: connect(new LtsMessageDialogNew(this, title, message), &LtsMessageDialogNew::ok, this, &MyDialog::mySlot);
* This Popup-Dialog closes automatically if any button on it is clicked.
* Keep in mind that as this dialog does not block, following code gets executed right afetr creating this dialog.
*/
class LTSWIDGETS_EXPORT LtsMessageDialogNew : public QObject
{
Q_OBJECT
public:
explicit LtsMessageDialogNew(QWidget* parent,
const QString &message,
const QString &title = QString(),
const Behaviour behaviour = Behaviour::Modal,
const Buttons buttons = Button::Ok,
const QHash<Button, QString> &buttonTexts = QHash<Button, QString>(),
bool deleteOnClose = true /* retain c++ conformity by this convenience bool */);
private:
LtsFramelessDialog *m_dialog;
QPushButton *addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent);
signals:
void ok();
void yes();
void no();
void apply();
void discard();
void cancel();
};
} // namespace LtsWidgets
Q_DECLARE_OPERATORS_FOR_FLAGS(LtsWidgets::Buttons)
+155
View File
@@ -0,0 +1,155 @@
#include "lts_message_dialog_helper.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QEventLoop>
namespace LtsWidgets
{
LtsMessageDialogHelper::LtsMessageDialogHelper(QObject *parent)
: QObject(parent)
, m_result(Button::Cancel)
, m_dialog(nullptr)
{}
Button LtsMessageDialogHelper::showMessage(QWidget *parent,
const QString &message,
const QString &title,
Behaviour behaviour,
Buttons buttons,
const QHash<Button, QString> &buttonTexts)
{
m_dialog = new LtsFramelessDialog(parent, behaviour);
m_dialog->setAttribute(Qt::WA_DeleteOnClose); // Message Dialogs are loose instances that must delete theirselves, because noone keeps a pointer to the message dialog
QVBoxLayout *mainLayout = new QVBoxLayout(m_dialog);
QString text;
if (!title.isEmpty()) // title given?
{
text = QStringLiteral("<b>%1</b><br><br>").arg(title);
}
text += message;
QLabel *messageLabel = new QLabel(text, m_dialog);
messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); // make text selectable by mouse
mainLayout->addWidget(messageLabel);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->setMargin(0);
// TODO: FIXME: can this be called after the selection was made and the shadow just dies ?
connect(m_dialog, &LtsFramelessDialog::rejected, this, &LtsMessageDialogHelper::slotCancel); // respect that non-modal dialogs can be closed by clicking outside of it
if (buttons.testFlag(Button::Ok))
connect(addButton(buttonTexts.value(Button::Ok, tr("Ok")), buttonLayout, m_dialog, Button::Ok, parent),
&QPushButton::clicked, this, &LtsMessageDialogHelper::slotOkay);
if (buttons.testFlag(Button::Yes))
connect(addButton(buttonTexts.value(Button::Yes, tr("Yes")), buttonLayout, m_dialog, Button::Yes, parent),
&QPushButton::clicked, this, &LtsMessageDialogHelper::slotYes);
if (buttons.testFlag(Button::No))
connect(addButton(buttonTexts.value(Button::No, tr("No")), buttonLayout, m_dialog, Button::No, parent),
&QPushButton::clicked, this, &LtsMessageDialogHelper::slotNo);
if (buttons.testFlag(Button::Apply))
connect(addButton(buttonTexts.value(Button::Apply, tr("Apply")), buttonLayout, m_dialog, Button::Apply, parent),
&QPushButton::clicked, this, &LtsMessageDialogHelper::slotApply); // same as yes
if (buttons.testFlag(Button::Discard))
connect(addButton(buttonTexts.value(Button::Discard, tr("Discard")), buttonLayout, m_dialog, Button::Discard, parent),
&QPushButton::clicked, this, &LtsMessageDialogHelper::slotDiscard); // same as no
if (buttons.testFlag(Button::Cancel))
connect(addButton(buttonTexts.value(Button::Cancel, tr("Cancel")), buttonLayout, m_dialog, Button::Cancel, parent),
&QPushButton::clicked, this, &LtsMessageDialogHelper::slotCancel);
QWidget *buttonLayoutWidget = new QWidget;
buttonLayoutWidget->setLayout(buttonLayout);
mainLayout->addWidget(buttonLayoutWidget);
m_dialog->show();
m_dialog->raise();
// this class is called a dialog, but is not a QDialog, so there is no event loop to wait for a result to return, so we create our own event loop
QEventLoop loop;
connect(this, &LtsMessageDialogHelper::signalQuitEventLoop, &loop, &QEventLoop::quit, Qt::QueuedConnection);
loop.exec(); // now wait for new result value before returning
return m_result;
}
void LtsMessageDialogHelper::slotOkay()
{
m_dialog->hide();
m_result = Button::Ok;
emit signalQuitEventLoop();
}
void LtsMessageDialogHelper::slotYes()
{
m_dialog->hide();
m_result = Button::Yes;
emit signalQuitEventLoop();
}
void LtsMessageDialogHelper::slotNo()
{
m_dialog->hide();
m_result = Button::No;
emit signalQuitEventLoop();
}
void LtsMessageDialogHelper::slotApply()
{
m_dialog->hide();
m_result = Button::Apply;
emit signalQuitEventLoop();
}
void LtsMessageDialogHelper::slotDiscard()
{
m_dialog->hide();
m_result = Button::Discard;
emit signalQuitEventLoop();
}
void LtsMessageDialogHelper::slotCancel()
{
m_dialog->hide();
m_result = Button::Cancel;
emit signalQuitEventLoop();
}
QPushButton* LtsMessageDialogHelper::addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent)
{
QPushButton* btn = new QPushButton(text, parent);
connect(btn, &QPushButton::clicked, dialog, &LtsFramelessDialog::accept);
thatLayout->addWidget(btn);
if (button == Button::Ok ||
button == Button::Cancel) // ok and cancel should not be used together, so there is no check needed (as only one button can be the default button)
{
btn->setDefault(true);
}
return btn;
}
} // namespace LtsWidgets
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "lts_frameless_dialog.h"
#include "lts_message_dialog.h"
#include <QHash>
#include <QObject>
#include <QString>
class QPushButton;
class QHBoxLayout;
namespace LtsWidgets
{
/**
* @brief The LtsMessageDialogHelper class cannot be exported and only is a helper class which is actually instantiated, shows the LtsMessageDialog.
* To provide the blocking call to showMessage (call with return type), an event loop is created and exited when the user has selected a button to close the dialog.
* If the behaviour is non-modal, closing the Dialog by clicking outside of it will return Button::Cancel.
*/
class LtsMessageDialogHelper : public QObject
{
Q_OBJECT
public:
explicit LtsMessageDialogHelper(QObject *parent = nullptr);
~LtsMessageDialogHelper() = default;
Button showMessage(QWidget *parent,
const QString &message,
const QString &title,
Behaviour behaviour,
LtsWidgets::Buttons buttons,
const QHash<Button, QString> &buttonTexts);
private:
QPushButton *addButton(const QString &text, QHBoxLayout *thatLayout, LtsFramelessDialog *dialog, const Button &button, QWidget *parent);
Button m_result;
LtsFramelessDialog *m_dialog;
private slots:
void slotOkay();
void slotYes();
void slotNo();
void slotCancel();
void slotApply();
void slotDiscard();
signals:
void signalQuitEventLoop();
};
} // namespace LtsWidgets
+114
View File
@@ -0,0 +1,114 @@
#include "lts_msec_date_time_edit.h"
namespace LtsWidgets
{
LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(QWidget *parent)
: QDateTimeEdit(parent)
{
setWrapping(true);
setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz"));
connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished);
}
LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(const QDateTime &dt, QWidget *parent)
: QDateTimeEdit(dt, parent)
{
setWrapping(true);
setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz"));
connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished);
}
LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(const QDate &d, QWidget *parent)
: QDateTimeEdit(d, parent)
{
setWrapping(true);
setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz"));
connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished);
}
LtsMSecDateTimeEdit::LtsMSecDateTimeEdit(const QTime &t, QWidget *parent)
: QDateTimeEdit(t, parent)
{
setWrapping(true);
setDisplayFormat(QLatin1String("dd.MM.yy HH:mm:ss.zzz"));
connect(this, &LtsMSecDateTimeEdit::editingFinished, this, &LtsMSecDateTimeEdit::onEditingFinished);
}
void LtsMSecDateTimeEdit::stepBy(int steps)
{
switch (currentSection()) {
case QDateTimeEdit::MonthSection:
QDateTimeEdit::setDateTime(dateTime().addMonths(steps));
break;
case QDateTimeEdit::YearSection:
QDateTimeEdit::setDateTime(dateTime().addYears(steps));
break;
case QDateTimeEdit::DaySection:
QDateTimeEdit::setDateTime(dateTime().addDays(steps));
break;
case QDateTimeEdit::HourSection:
QDateTimeEdit::setDateTime(dateTime().addSecs(steps * 3600));
break;
case QDateTimeEdit::MinuteSection:
QDateTimeEdit::setDateTime(dateTime().addSecs(steps * 60));
break;
case QDateTimeEdit::SecondSection:
QDateTimeEdit::setDateTime(dateTime().addSecs(steps));
break;
case QDateTimeEdit::MSecSection:
QDateTimeEdit::setDateTime(dateTime().addMSecs(steps * m_msecStepSize));
break;
case QDateTimeEdit::NoSection:
case QDateTimeEdit::AmPmSection:
case QDateTimeEdit::TimeSections_Mask:
case QDateTimeEdit::DateSections_Mask:
default:
break;
}
}
qint64 LtsMSecDateTimeEdit::msecsSinceEpoch() const
{
return dateTime().toMSecsSinceEpoch() - (m_timezoneHoursOffset * 3600 * 1000); // ..UGH..
}
void LtsMSecDateTimeEdit::setTimezoneHoursOffset(int timezoneHoursOffset)
{
qint64 b4 = msecsSinceEpoch();
m_timezoneHoursOffset = timezoneHoursOffset; // set timezone AFTER reading the old value (otherwise timezone conversion would fail)
setMsecsSinceEpoch(b4); // apply timezone by re-setting
}
void LtsMSecDateTimeEdit::setDateTime(const QDateTime &dateTime)
{
// TODO: doesn't make sense, as toMSecsSinceEpoch returns the *UTC version* of the dt, so the timespec stuff does nothing
setMsecsSinceEpoch(QDateTime::fromMSecsSinceEpoch(dateTime.toMSecsSinceEpoch(), Qt::TimeSpec::OffsetFromUTC, m_timezoneHoursOffset * 3600).toMSecsSinceEpoch());
}
void LtsMSecDateTimeEdit::setMsecsSinceEpoch(qint64 msecs)
{
QDateTimeEdit::setDateTime(QDateTime::fromMSecsSinceEpoch(msecs, Qt::TimeSpec::OffsetFromUTC, m_timezoneHoursOffset * 3600));
}
int LtsMSecDateTimeEdit::msecStepSize() const
{
return m_msecStepSize;
}
void LtsMSecDateTimeEdit::setMsecStepSize(int msecStepSize)
{
m_msecStepSize = msecStepSize;
}
void LtsMSecDateTimeEdit::onEditingFinished()
{
qint64 val = msecsSinceEpoch();
if (val % m_msecStepSize == 0)
return;
val = (val + (m_msecStepSize/2) ) / m_msecStepSize * m_msecStepSize;
setMsecsSinceEpoch(val);
}
} // namespace
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <QDateTimeEdit>
#include "lts_widgets_export.h"
namespace LtsWidgets {
class LTSWIDGETS_EXPORT LtsMSecDateTimeEdit : public QDateTimeEdit
{
Q_OBJECT
Q_PROPERTY(int msecStepSize READ msecStepSize WRITE setMsecStepSize)
public:
explicit LtsMSecDateTimeEdit(QWidget *parent = nullptr);
explicit LtsMSecDateTimeEdit(const QDateTime &dt, QWidget *parent = nullptr);
explicit LtsMSecDateTimeEdit(const QDate &d, QWidget *parent = nullptr);
explicit LtsMSecDateTimeEdit(const QTime &t, QWidget *parent = nullptr);
qint64 msecsSinceEpoch() const;
int timezoneHoursOffset() const { return m_timezoneHoursOffset; }
void stepBy(int steps) override;
int msecStepSize() const;
public slots:
void setDateTime(const QDateTime &dateTime);
void setMsecsSinceEpoch(qint64 msecs);
void setMsecStepSize(int msecStepSize);
void setTimezoneHoursOffset(int timezoneHoursOffset);
private slots:
void onEditingFinished();
private:
int m_msecStepSize = 10;
int m_timezoneHoursOffset = 0;
};
} // namespace
+334
View File
@@ -0,0 +1,334 @@
/**
* @RDE_HEADER@
* SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value
* $Id$
* $HeadURL$
* $Author$
*
* Created by Malte Diers DER LOSER, NLSE, 2018-05-07
*/
#include "lts_multi_range_spinbox.h"
#include <QEvent>
#include <QWheelEvent>
#include <cmath>
namespace LtsWidgets
{
LtsMultiRangeSpinBox::LtsMultiRangeSpinBox(QWidget *parent)
: QSpinBox(parent)
{
m_curVal = minimum();
}
/*!
* \brief LtsSpinBoxNew::validate
* \param input
* \param pos
* \brief only used if enter pressed
* \return
*/
QValidator::State LtsMultiRangeSpinBox::validate(QString &input, int &pos) const
{
// QIntValidator validator;
// int minimumVal = minimum();
// int maximumVal = maximum();
// validator.setBottom(minimum());
// validator.setTop(maximum());
// QValidator::State state = validator.validate(input, pos);
// if (state == QValidator::Invalid ||
// state == QValidator::Intermediate)
// {
// return state;
// }
// // Now we're sure, it's an int. Therefore toInt() will always be okay:
// int value = input.toInt();
// value -= minimum();
// return (value % singleStep() == 0) ? QValidator::Acceptable : QValidator::Intermediate;
return QValidator::Acceptable;
}
void LtsMultiRangeSpinBox::fixup(QString &str) const
{
// QSpinBox::fixup(str);
// if (singleStep() == 1)
// {
// return;
// }
// bool bOk = false;
// int value = str.toInt(&bOk);
// if (!bOk)
// {
// return;
// }
// fixupValue(value);
// str = QString::number(value);
}
QValidator::State LtsMultiRangeSpinBox::validateAll(QString &input, int &pos)
{
for(QPair<int,int> range : qAsConst(m_rangesList))
{
QIntValidator validator(range.first, range.second);
QValidator::State valState = validator.validate(input, pos);
if (valState == QValidator::Acceptable)
{
return valState;
}
}
return QValidator::Invalid;
//return validate(input, pos);
}
/*!
* \brief LtsSpinBoxNew::setGaps
* \param gaps
* \author Niklas Schmidt
*/
QVector<QPair<int,int>> LtsWidgets::LtsMultiRangeSpinBox::sortRanges(const QVector<QPair<int, int> > &ranges)
{
QVector<QPair<int, int>> rangesEditable = ranges;
QVector<QPair<int, int>> result;
std::sort(rangesEditable.begin(), rangesEditable.end());
QVector<QPair<int, int>>::iterator it = rangesEditable.begin();
QPair<int,int> current = *(it)++;
while (it != rangesEditable.end())
{
if (current.second > it->first)
current.second = std::max(current.second, it->second);
else
{
result.push_back(current);
current = *(it);
}
++it;
}
result.push_back(current);
return result;
}
void LtsMultiRangeSpinBox::setRanges(const QVector<QPair<int, int>> &ranges)
{
if(ranges.empty())
return;
m_rangesList = sortRanges(ranges);
QSpinBox::setMaximum(m_rangesList.last().second);
QSpinBox::setMinimum(m_rangesList.first().first);
QStringList rangeStrings;
rangeStrings.reserve(m_rangesList.size());
for (const QPair<int,int> &range : qAsConst(m_rangesList))
rangeStrings << tr("%1-%2%3").arg(range.first).arg(range.second).arg(suffix());
setToolTip(tr("Ranges:\n%1").arg(rangeStrings.join(QStringLiteral(",\n"))));
}
void LtsMultiRangeSpinBox::setRange(int min, int max)
{
setRanges({{min,max}});
}
void LtsMultiRangeSpinBox::setMinimum(int min)
{
auto ranges = m_rangesList;
ranges.first().first = min;
setRanges(ranges);
}
void LtsMultiRangeSpinBox::setMaximum(int max)
{
auto ranges = m_rangesList;
ranges.last().second = max;
setRanges(ranges);
}
int LtsMultiRangeSpinBox::fixupValue(int value) const
{
int nearestValue = INT_MAX;
for(QPair<int,int> range : m_rangesList)
{
if(std::abs(range.first - value) < std::abs(nearestValue - value))
nearestValue = range.first;
if(std::abs(range.second - value) < std::abs(nearestValue - value))
nearestValue = range.second;
}
value = nearestValue;
int buffer = value;
int remainder = (value - minimum()) % singleStep();
if (2 * remainder > singleStep())
{
value += singleStep();
}
value -= remainder;
if(buffer + singleStep() > value + singleStep())
{
value+= singleStep();
}
if (value > maximum())
{
value -= singleStep();
}
return value;
}
int LtsMultiRangeSpinBox::fixupValue(int value, bool higher) const
{
int nearestValue = INT_MAX;
for(QPair<int,int> range : m_rangesList)
{
if(std::abs(range.first - value) < std::abs(nearestValue - value))
{
if(higher)
{
if(range.second < value)
continue;
} else
if(range.second > value)
continue;
nearestValue = range.first;
}
if(std::abs(range.second - value) < std::abs(nearestValue - value))
{
if(higher)
{
if(range.second < value)
continue;
} else
if(range.second > value)
continue;
nearestValue = range.second;
}
}
value = nearestValue;
int curVal = value;
int remainder = (value - minimum()) % singleStep();
if (2 * remainder > singleStep())
{
value += singleStep();
}
value -= remainder;
if(curVal + singleStep() > value + singleStep())
{
value+= singleStep();
}
if (value > maximum())
{
value -= singleStep();
}
return value;
}
void LtsMultiRangeSpinBox::focusOutEvent(QFocusEvent *event)
{
QSpinBox::focusOutEvent(event);
QString value = QString::number(this->value());
int length = value.length();
QValidator::State valState = validateAll(value, length);
if(valState == QValidator::State::Invalid)
{
this->setValue(fixupValue(value.toInt()));
}
}
void LtsMultiRangeSpinBox::wheelEvent(QWheelEvent *event)
{
QSpinBox::wheelEvent(event);
QString valueStr = QString::number(this->value());
int length = valueStr.length();
QValidator::State valState = validateAll(valueStr, length);
if(valState == QValidator::State::Invalid)
{
/*!
Check if the new value is lower or higher the past value
Example: Gap between 50 and 100. Value changed from 100 to 99 requested
So the new value is lower so the SpinBox need to be corrected down to 49
!*/
if(this->value() < m_curVal)
{
m_curVal = fixupValue(this->value(), false);
setValue(m_curVal);
}
else
{
m_curVal = fixupValue(this->value(), true);
setValue(m_curVal);
}
}
}
void LtsMultiRangeSpinBox::mousePressEvent(QMouseEvent *event)
{
QSpinBox::mousePressEvent(event);
QString valueStr = QString::number(this->value());
int length = valueStr.length();
QValidator::State valState = validateAll(valueStr, length);
if(valState == QValidator::State::Invalid)
{
/*!
Check if the new value is lower or higher the past value
Example: Gap between 50 and 100. Value changed from 100 to 99 requested
So the new value is lower so the SpinBox need to be corrected down to 49
!*/
if(this->value() < m_curVal)
{
m_curVal = fixupValue(this->value(), false);
setValue(m_curVal);
}
else
{
m_curVal = fixupValue(this->value(), true);
setValue(m_curVal);
}
}
}
void LtsMultiRangeSpinBox::keyPressEvent(QKeyEvent *event)
{
QSpinBox::keyPressEvent(event);
QString value = QString::number(this->value());
int length = value.length();
QValidator::State valState = validateAll(value, length);
if(valState == QValidator::State::Invalid)
{
setValue(fixupValue(value.toInt()));
}
}
} // namespace LtsWidgets
+59
View File
@@ -0,0 +1,59 @@
/**
* @RDE_HEADER@
* SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value
* $Id$
* $HeadURL$
* $Author$
*
* Created by Malte Diers, NLSE, 2018-05-07
*/
#pragma once
#include "lts_widgets_export.h"
#include <QSet>
#include <QSpinBox>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsMultiRangeSpinBox : public QSpinBox
{
Q_OBJECT
public:
explicit LtsMultiRangeSpinBox(QWidget *parent = nullptr);
~LtsMultiRangeSpinBox() = default;
public slots:
void setRanges(const QVector<QPair<int, int>> &ranges);
void setRange(int min, int max);
void setMinimum(int min);
void setMaximum(int max);
protected:
QValidator::State validate(QString &input, int &pos) const override;
void fixup(QString &str) const override;
void focusOutEvent(QFocusEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private:
static QVector<QPair<int,int>> sortRanges(const QVector<QPair<int, int> > &ranges);
QValidator::State validateAll(QString &input, int &pos);
//void buildRegex();
int fixupValue(int value) const;
int fixupValue(int value, bool higher) const;
private:
QVector<QPair<int, int>> m_rangesList;
int m_curVal = minimum();
QRegExp m_regex;
};
}
+198
View File
@@ -0,0 +1,198 @@
#include "lts_notification_box.h"
#include "ui_lts_notification_box.h"
#include <QPainter>
#include <QLinearGradient>
#include <QColor>
#include <QGraphicsDropShadowEffect>
#include <QStyleOption>
#include <QStyle>
#ifdef NOTIBOX_DEBUG
#include <QDebug>
#endif
namespace LtsWidgets
{
const int LtsNotificationBox::margin = 14;
LtsNotificationBox::LtsNotificationBox(QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::NotiBox)
{
m_ui->setupUi(this);
m_ui->lblText->setText({});
connect(m_ui->lblText, &QLabel::linkActivated,
this, [this](const QString &link)
{
emit signalLinkClicked(link);
});
connect(m_ui->tbClose, &QToolButton::clicked,
this, &LtsNotificationBox::signalClose);
connect(m_ui->tbAck, &QToolButton::clicked,
this, &LtsNotificationBox::signalAck);
setObjectName(QStringLiteral("NotiBox"));
QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
shadow->setOffset(0, 0);
shadow->setBlurRadius(margin*2);
shadow->setColor(QColor(25, 25, 25));
setGraphicsEffect(shadow);
}
void LtsNotificationBox::message(const QString &title, const QString &text, MessageType type)
{
static QString rgbBaseString(QStringLiteral("%1, %2, %3"));
static QString closeDefault(QStringLiteral("close.svg"));
static QString closeDark(QStringLiteral("close_dark.svg"));
static QString ackDefault(QStringLiteral("ack.svg"));
static QString ackDark(QStringLiteral("ack_dark.svg"));
static QString lightTextColor(QStringLiteral("fbfbfb"));
static QString darkTextColor(QStringLiteral("2c3642"));
static QColor errorStart(254, 45, 102);
static QColor errorEnd(255, 92, 61);
static QColor successStart(43, 167, 70);
static QColor successEnd(130, 180, 45);
static QColor infoStart(74, 74, 74);
static QColor infoEnd(43, 43, 43);
static QColor warningStart(253, 151, 4);
static QColor warningEnd(253, 202, 0);
QColor start;
QColor end;
QString close;
QString closeHover;
QString ack;
QString ackHover;
QString textColor;
switch (type)
{
case MessageType::Error:
{
start = errorStart;
end = errorEnd;
close = closeDefault;
closeHover = closeDark;
ack = ackDefault;
ackHover = ackDark;
textColor = lightTextColor;
m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/error.svg")));
break;
}
case MessageType::Success:
{
start = successStart;
end = successEnd;
close = closeDefault;
closeHover = closeDark;
ack = ackDefault;
ackHover = ackDark;
textColor = lightTextColor;
m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/success.svg")));
break;
}
case MessageType::Info:
{
start = infoStart;
end = infoEnd;
close = closeDefault;
closeHover = closeDark;
ack = ackDefault;
ackHover = ackDark;
textColor = lightTextColor;
m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/info.svg")));
break;
}
case MessageType::Warning:
{
start = warningStart;
end = warningEnd;
close = closeDark;
closeHover = closeDefault;
ack = ackDark;
ackHover = ackDefault;
textColor = darkTextColor;
m_ui->lblImage->setPixmap(QPixmap(QStringLiteral(":/warning.svg")));
break;
}
}
QStringList css;
css << QStringLiteral("#NotiBox { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop: 0 rgb(%1), stop: 1 rgb(%2)); border: 1px solid rgb(%3); border-radius: 8px; margin: %4px }").
arg(rgbBaseString.arg(start.red()).arg(start.green()).arg(start.blue()),
rgbBaseString.arg(end.red()).arg(end.green()).arg(end.blue()),
rgbBaseString.arg(end.red()).arg(end.green()).arg(end.blue())).
arg(margin);
css << QStringLiteral("QToolButton#tbClose { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(close);
css << QStringLiteral("QToolButton#tbClose:hover { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(closeHover);
css << QStringLiteral("QToolButton#tbAck { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(ack);
css << QStringLiteral("QToolButton#tbAck:hover { border-image: url(:/%1) 0 0 0 0 stretch stretch; border-width: 0px; background-color: transparent; }").arg(ackHover);
#ifdef NOTIBOX_DEBUG
qWarning() << "css" << css;
qWarning() << "exists?" << QFile::exists(QStringLiteral(":/close.svg"));
qWarning() << "exists?" << QFile::exists(QStringLiteral(":/close_dark.svg"));
qWarning() << "exists?" << QFile::exists(QStringLiteral(":/error.svg"));
qWarning() << "exists?" << QFile::exists(QStringLiteral(":/info.svg"));
qWarning() << "exists?" << QFile::exists(QStringLiteral(":/warning.svg"));
qWarning() << "exists?" << QFile::exists(QStringLiteral(":/success.svg"));
#endif
m_ui->lblImage->setStyleSheet(QString());
setStyleSheet(css.join(QStringLiteral(" ")));
m_ui->lblText->setText(QStringLiteral("<p><span style='font-size:14pt; font-weight:600; color:#%1;'>%2</span></p><p><span style='font-size:14pt; color:#%3;'>%4</span></p>").arg(textColor, title, textColor, text));
show();
}
void LtsNotificationBox::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
} // namespace LtsWidgets
//
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include "lts_widgets_export.h"
#include <QWidget>
#include <QPainterPath>
class QPaintEvent;
class QResizeEvent;
namespace Ui
{
class NotiBox;
}
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsNotificationBox : public QWidget
{
Q_OBJECT
public:
enum class MessageType
{
Success,
Error,
Warning,
Info,
};
explicit LtsNotificationBox(QWidget *parent = nullptr);
void message(const QString &title, const QString &text, MessageType type = MessageType::Error);
signals:
void signalClose();
void signalAck();
void signalLinkClicked(const QString &link);
protected:
void paintEvent(QPaintEvent *) override;
private:
Ui::NotiBox *m_ui;
QPainterPath roundRectPath;
const static int margin;
};
} // namespace LtsWidgets
+182
View File
@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NotiBox</class>
<widget class="QWidget" name="NotiBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>670</width>
<height>130</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>670</width>
<height>130</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>670</width>
<height>130</height>
</size>
</property>
<property name="windowTitle">
<string>Notification</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>:/rde_icon_256.png</normaloff>:/rde_icon_256.png</iconset>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0">
<property name="spacing">
<number>20</number>
</property>
<property name="leftMargin">
<number>40</number>
</property>
<property name="topMargin">
<number>18</number>
</property>
<property name="rightMargin">
<number>18</number>
</property>
<property name="bottomMargin">
<number>18</number>
</property>
<item>
<widget class="QLabel" name="lblImage">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblText">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:14pt; font-weight:600; color:#eeeeee;&quot;&gt;Error Message&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:14pt; color:#eeeeee;&quot;&gt;This is an error message. looooooooooooooooooooong text&lt;br/&gt;With 2 lines!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="tbAck">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="special" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="tbClose">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="special" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+121
View File
@@ -0,0 +1,121 @@
/**
* @RDE_HEADER@
* User Menu on active sessions (logout, etc.)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-08-17
*/
#include "lts_operator_menu.h"
#include "ui_lts_operator_menu.h"
#include "lts_info_dialog.h"
#include <ngltranslator/ngltranslator.h>
#include <QPushButton>
#include <QProcess>
#include <QHostInfo>
namespace LtsWidgets
{
LtsOperatorMenu::LtsOperatorMenu(const QString &operatorName, const QString &workspaceId, bool debug, int screenCount, QWidget *parent)
: LtsFramelessDialog(parent)
, m_ui(new Ui::LtsOperatorMenu)
{
m_ui->setupUi(this);
m_ui->lbOperatorName->setText(operatorName);
m_ui->lbHostName->setText(QHostInfo::localHostName());
if (screenCount < 2)
m_ui->pbSwap->hide(); // swaps only possible if more than 1 screen is available
if (debug)
m_ui->lbWorkspaceId->setText(tr("Workspace: >>>%1<<<").arg(workspaceId));
else
m_ui->lbWorkspaceId->hide();
connect(m_ui->pbOperatorSettings, &QPushButton::clicked, this, &LtsOperatorMenu::signalShowOperatorSettings);
connect(m_ui->pbOperatorSettings, &QPushButton::clicked, this, &LtsOperatorMenu::close);
connect(m_ui->pbWindow, &QPushButton::clicked, this, &LtsOperatorMenu::onTerminalButtonClicked);
connect(m_ui->pbWindow, &QPushButton::clicked, this, &LtsOperatorMenu::close);
connect(m_ui->pbLogout, &QPushButton::clicked, this, &LtsOperatorMenu::signalLogout);
connect(m_ui->pbExit, &QPushButton::clicked, this, &LtsOperatorMenu::signalExit);
connect(m_ui->pbCancel, &QPushButton::clicked, this, &LtsOperatorMenu::close);
connect(m_ui->pbSwap, &QPushButton::clicked, this, &LtsOperatorMenu::signalSwap);
connect(m_ui->pbSwap, &QPushButton::clicked, this, &LtsOperatorMenu::close);
connect(m_ui->pbAbout, &QPushButton::clicked, this, &LtsOperatorMenu::slotShowInfoDialog);
connect(m_ui->pbAbout, &QPushButton::clicked, this, &LtsOperatorMenu::close);
if (NGL::translationLocale() == "de_DE")
m_ui->pbGer->setChecked(true);
else
m_ui->pbInt->setChecked(true);
connect(m_ui->pbGer, &QPushButton::clicked,
this, &LtsOperatorMenu::onLanguageChange);
connect(m_ui->pbInt, &QPushButton::clicked,
this, &LtsOperatorMenu::onLanguageChange);
#ifndef LTS_DEBUG
m_ui->debugWidget->hide();
#endif
}
LtsOperatorMenu::~LtsOperatorMenu()
{
delete m_ui;
}
void LtsOperatorMenu::slotShowInfoDialog()
{
LtsInfoDialog *infoDialog = new LtsInfoDialog();
infoDialog->setAttribute(Qt::WA_DeleteOnClose);
infoDialog->show();
}
void LtsOperatorMenu::onTerminalButtonClicked()
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString lang = QString::fromLatin1(NGL::translationLocale());
lang.append(QStringLiteral(".UTF-8"));
env.insert(QStringLiteral("LANG"), lang);
QProcess process;
process.setProcessEnvironment(env); //Da startDetached static ist wird hier drauf nicht zugegriffen
QStringList args;
args << QStringLiteral("--geometry") << QStringLiteral("800x800+200+100");
if (QApplication::queryKeyboardModifiers() == Qt::ControlModifier)
process.startDetached(QStringLiteral("xterm"), args);
else
process.startDetached(QStringLiteral("konsole"), args);
}
void LtsOperatorMenu::setTerminalButton(bool bVisible)
{
m_ui->pbWindow->setVisible(bVisible);
}
void LtsOperatorMenu::setOperatorSettingsButton(bool bVisible)
{
m_ui->pbOperatorSettings->setVisible(bVisible);
}
void LtsOperatorMenu::onLanguageChange()
{
if (m_ui->pbGer->isChecked())
NGL::setTranslationLocale(QByteArray("de_DE"));
if (m_ui->pbInt->isChecked())
NGL::setTranslationLocale(QByteArray("en_GB"));
}
} // namespace LtsWidgets
+49
View File
@@ -0,0 +1,49 @@
/**
* @RDE_HEADER@
* User Menu on active sessions (logout, etc.)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-08-17
*/
#pragma once
#include "lts_frameless_dialog.h"
#include "lts_widgets_export.h"
namespace Ui {
class LtsOperatorMenu;
}
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsOperatorMenu : public LtsFramelessDialog
{
Q_OBJECT
public:
explicit LtsOperatorMenu(const QString &operatorName, const QString &workspaceId, bool debug, int screenCount, QWidget *parent);
~LtsOperatorMenu();
void setTerminalButton(bool bVisible);
void setOperatorSettingsButton(bool bVisible);
private:
Ui::LtsOperatorMenu *m_ui;
signals:
void signalLogout();
void signalExit();
void signalShowOperatorSettings();
void signalSwap();
private slots:
void onTerminalButtonClicked();
void onLanguageChange();
void slotShowInfoDialog();
};
} // namespace LtsWidgets
+247
View File
@@ -0,0 +1,247 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsOperatorMenu</class>
<widget class="QWidget" name="LtsOperatorMenu">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>310</width>
<height>504</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Operator Menu</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lbOperatorName">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string notr="true">%OPERATORNAME%</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbHostName">
<property name="text">
<string notr="true">%HOSTNAME%</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbWorkspaceId">
<property name="text">
<string notr="true">Workspace: %WORKSPACEID%</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbOperatorSettings">
<property name="text">
<string>Operator Settings</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbWindow">
<property name="text">
<string>Terminal Window</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbSwap">
<property name="text">
<string>Swap with another window</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbLogout">
<property name="text">
<string>Operator Logout</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbExit">
<property name="text">
<string>Exit Application</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pbAbout">
<property name="text">
<string>About</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="debugWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbDebug">
<property name="text">
<string>For Debug only</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pbGer">
<property name="toolTip">
<string>Deutsch - German</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources/lts_resources.qrc">
<normaloff>:/Germany-Flag.png</normaloff>:/Germany-Flag.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbInt">
<property name="toolTip">
<string>English - Englisch (International)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources/lts_resources.qrc">
<normaloff>:/Great-Britain-Flag.png</normaloff>:/Great-Britain-Flag.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>18</width>
<height>29</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="resources/lts_resources.qrc"/>
</resources>
<connections/>
</ui>
+54
View File
@@ -0,0 +1,54 @@
/**
* @RDE_HEADER@
* An overlay for LtsDialog's setOverlay(..) which shows a 'loading bar'
* (a progress bar in a special state) and a default label "Loading ...", which
* can be altered
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Lennart Wille, SLTE, 2022-07-07
*/
#include "lts_overlay_loading.h"
#include "ui_lts_overlay_loading.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
namespace LtsWidgets
{
LtsOverlayLoading::LtsOverlayLoading(QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::LtsOverlayLoading)
{
m_ui->setupUi(this);
setCancelButtonVisible(false);
connect(m_ui->pbCancel, &QPushButton::clicked,
this, [this](){ setCancelButtonEnabled(false); });
connect(m_ui->pbCancel, &QPushButton::clicked,
this, &LtsOverlayLoading::cancel);
}
void LtsOverlayLoading::setText(const QString &text)
{
m_ui->label->setText(text);
}
void LtsOverlayLoading::setCancelButtonVisible(bool visible)
{
m_ui->pbCancel->setVisible(visible);
}
void LtsOverlayLoading::setCancelButtonEnabled(bool enabled)
{
m_ui->pbCancel->setEnabled(enabled);
}
} // namespace LtsWidgets
+48
View File
@@ -0,0 +1,48 @@
/**
* @RDE_HEADER@
* An overlay for LtsDialog's setOverlay(..) which shows a 'loading bar'
* (a progress bar in a special state) and a default label "Loading ...", which
* can be altered
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Lennart Wille, SLTE, 2022-07-07
*/
#pragma once
#include "lts_widgets_export.h"
#include <QWidget>
namespace Ui { class LtsOverlayLoading; }
namespace LtsWidgets
{
/**
* @brief The LtsOverlayLoading class is an overlay for LtsDialog's
* setOverlay(..) which shows a 'loading bar' (a progress bar in a special
* state), a default label "Loading ..." - which can be altered with
* 'setText(..)' - and a cancel button (hidden by default) that emits the
* 'cancel' signal.
*/
class LTSWIDGETS_EXPORT LtsOverlayLoading : public QWidget
{
Q_OBJECT
public:
explicit LtsOverlayLoading(QWidget *parent = nullptr);
void setText(const QString &text);
void setCancelButtonVisible(bool visible);
void setCancelButtonEnabled(bool enabled);
signals:
void cancel();
private:
Ui::LtsOverlayLoading *m_ui;
};
}
+76
View File
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsOverlayLoading</class>
<widget class="QWidget" name="LtsOverlayLoading">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>370</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>370</width>
<height>120</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>555</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="99999,0">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Loading ...</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QProgressBar" name="pbarMain">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCancel">
<property name="text">
<string>&amp;Cancel</string>
</property>
<property name="shortcut">
<string>Shift+C</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+345
View File
@@ -0,0 +1,345 @@
/**
* @RDE_HEADER@
* Crop pictures
* $Id$
* $HeadURL$
* $Author$
* Created by Raphael Becker
*/
#include "lts_picture_view.h"
#include <QMouseEvent>
#include <QGraphicsPixmapItem>
#include <QGraphicsColorizeEffect>
#include <QRubberBand>
using namespace LtsWidgets;
LtsPictureView::LtsPictureView(QWidget *parent)
: QGraphicsView(parent)
, m_gsPicture(new QGraphicsScene(this))
, m_rubberband(new QRubberBand(QRubberBand::Rectangle, this))
, m_image(nullptr)
, m_origimage(nullptr)
{
m_mouseActive = false;
m_moveable = false;
m_aspectRatioEnabled = true;
m_s_inEdit = false;
m_aspectRatio = 1;
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
LtsPictureView::~LtsPictureView()
{
delete m_rubberband;
}
void LtsPictureView::setAspectRatio(double aspectRatio)
{
m_aspectRatio = aspectRatio;
newRubberBand();
}
void LtsPictureView::ratioClicked(bool enabled)
{
m_aspectRatioEnabled = enabled;
if (enabled)
newRubberBand();
}
void LtsPictureView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if (m_rubberband->geometry().contains(event->pos()))
{
m_bandoffset = event->pos() - m_rubberband->pos();
m_moveable = true;
m_rubberband->show();
}
else
{
m_origin = event->pos();
m_curRectangle = QRect(m_origin.x(), m_origin.y(), 0, 0);
m_rubberband->setGeometry(m_curRectangle);
m_rubberband->show();
m_mouseActive = true;
}
}
else
{
hideRubberband();
newRubberBand();
m_mouseActive = false;
}
QGraphicsView::mousePressEvent(event);
}
void LtsPictureView::mouseMoveEvent(QMouseEvent *event)
{
if (m_moveable == true)
{
QPoint newCoord = event->pos() - m_bandoffset;
if (newCoord.x() < 0)
newCoord.setX(0);
if (newCoord.y() < 0)
newCoord.setY(0);
if (m_origimage->pixmap().width() < (newCoord.x() + m_rubberband->width()))
newCoord.setX(m_origimage->pixmap().width() - m_rubberband->width() + 1);
if (m_origimage->pixmap().height() < (newCoord.y() + m_rubberband->height()))
newCoord.setY(m_origimage->pixmap().height() - m_rubberband->height() + 1);
m_rubberband->move(newCoord);
QPoint move = m_rubberband->pos();
int left = move.x();
int top = move.y();
int width = m_rubberband->rect().width();
int height = m_rubberband->rect().height();
emit mouseMoved(QRect(left, top, width, height));
}
else
{
if (m_mouseActive)
{
QRect rect;
int xMax = event->pos().x();
int yMax = event->pos().y();
if (event->pos().x() > m_origimage->pixmap().width())
xMax = m_origimage->pixmap().width();
if (event->pos().x() < 0)
xMax = 0;
if (event->pos().y() > m_origimage->pixmap().height())
yMax = m_origimage->pixmap().height();
if (event->pos().y() < 0)
yMax = 0;
if (m_aspectRatioEnabled)
{
const int xMin = m_origin.x();
const int yMin = m_origin.y();
int xLengthAbs(std::abs(xMax - xMin));
int yLengthAbs(std::abs(yMax - yMin));
QPoint length;
if (xLengthAbs / m_aspectRatio > yLengthAbs * m_aspectRatio)
{
length.setX(xLengthAbs);
length.setY(length.x() / m_aspectRatio);
}
else
{
length.setY(yLengthAbs);
length.setX(length.y() * m_aspectRatio);
}
if (!(yMax - yMin < 0) && length.y() + m_origin.y() > m_origimage->pixmap().height())
{
length.setY(m_origimage->pixmap().height() - m_origin.y());
length.setX(length.y()* m_aspectRatio);
}
if (!(xMax - xMin < 0) && length.x() + m_origin.x() > m_origimage->pixmap().width())
{
length.setX(m_origimage->pixmap().width() - m_origin.x());
length.setY(length.x() / m_aspectRatio);
}
if (xMax - xMin < 0)
{
length.setX(length.x() * -1);
if (length.x() + m_origin.x() < 0)
{
length.setX(0 - m_origin.x());
length.setY((length.x() / m_aspectRatio) * -1);
}
}
if (yMax - yMin < 0)
{
length.setY(length.y() * -1);
if (length.y() + m_origin.y() < 0)
{
length.setY(0 - m_origin.y());
length.setX(length.y() * m_aspectRatio);
if (!(xMax - xMin < 0))
length.setX(length.x() * -1);
}
}
length = m_origin + length;
rect.setTopLeft(m_origin);
rect.setBottomRight(length);
}
else
{
rect.setTopLeft(m_origin);
rect.setBottomRight(QPoint(xMax, yMax));
}
m_curRectangle = rect.normalized();
m_rubberband->setGeometry(m_curRectangle);
emit mouseMoved(m_curRectangle);
}
}
QGraphicsView::mouseMoveEvent(event);
}
void LtsPictureView::mouseReleaseEvent(QMouseEvent *event)
{
m_moveable = false;
QGraphicsView::mouseReleaseEvent(event);
}
void LtsPictureView::hideRubberband()
{
m_rubberband->hide();
}
void LtsPictureView::newRubberBand()
{
delete m_rubberband;
m_rubberband = new QRubberBand(QRubberBand::Rectangle, this);
}
void LtsPictureView::setPicture(const QImage &image, const QPoint &pos, const bool flag)
{
if (m_image)
{
m_gsPicture->removeItem(m_image);
delete m_image;
}
if (flag)
{
if (m_origimage)
{
m_gsPicture->removeItem(m_origimage);
delete m_origimage;
}
QGraphicsOpacityEffect *opacity = new QGraphicsOpacityEffect;
opacity->setOpacity(0.2);
m_origimage = new QGraphicsPixmapItem(QPixmap::fromImage(image));
m_origimage->setGraphicsEffect(opacity);
m_gsPicture->addItem(m_origimage);
setMinimumSize(image.size());
setMaximumSize(image.size());
emit setLimits(m_origimage->pixmap().size(), true);
}
m_image = new QGraphicsPixmapItem(QPixmap::fromImage(image));
m_image->setPos(pos);
m_gsPicture->addItem(m_image);
setScene(m_gsPicture);
}
void LtsPictureView::leftChanged(int left)
{
if (m_s_inEdit || m_mouseActive)
return;
m_s_inEdit = true;
m_curRectangle.setLeft(left);
if(m_aspectRatioEnabled)
{
int newHeight = m_curRectangle.width() / m_aspectRatio;
if (m_origin.y() < m_curRectangle.bottom())
{
m_curRectangle.setHeight(newHeight);
}
else
{
int offset = m_curRectangle.height() - newHeight;
m_curRectangle.setTop(m_curRectangle.top() + offset);
}
}
m_rubberband->setGeometry(m_curRectangle);
emit mouseMoved(m_curRectangle);
m_s_inEdit = false;
}
void LtsPictureView::rightChanged(int right)
{
if (m_s_inEdit || m_mouseActive)
return;
m_s_inEdit = true;
m_curRectangle.setRight(right);
if(m_aspectRatioEnabled)
{
int newHeight = m_curRectangle.width() / m_aspectRatio;
if (m_origin.y() < m_curRectangle.bottom())
{
m_curRectangle.setHeight(newHeight);
}
else
{
int offset = m_curRectangle.height() - newHeight;
m_curRectangle.setTop(m_curRectangle.top() + offset);
}
}
m_rubberband->setGeometry(m_curRectangle);
emit mouseMoved(m_curRectangle);
m_s_inEdit = false;
}
void LtsPictureView::topChanged(int top)
{
if (m_s_inEdit || m_mouseActive)
return;
m_s_inEdit = true;
m_curRectangle.setTop(top);
if(m_aspectRatioEnabled)
{
int newWidth = m_curRectangle.height() * m_aspectRatio;
if (m_origin.x() <= m_curRectangle.left())
{
m_curRectangle.setWidth(newWidth);
}
else
{
int offset = m_curRectangle.width() - newWidth;
m_curRectangle.setLeft(m_curRectangle.left() + offset);
}
}
m_rubberband->setGeometry(m_curRectangle);
emit mouseMoved(m_curRectangle);
m_s_inEdit = false;
}
void LtsPictureView::bottomChanged(int bottom)
{
if (m_s_inEdit || m_mouseActive)
return;
m_s_inEdit = true;
m_curRectangle.setBottom(bottom);
if(m_aspectRatioEnabled)
{
int newWidth = m_curRectangle.height() * m_aspectRatio;
if (m_origin.x() <= m_curRectangle.left())
{
m_curRectangle.setWidth(newWidth);
}
else
{
int offset = m_curRectangle.width() - newWidth;
m_curRectangle.setLeft(m_curRectangle.left() + offset);
}
}
m_rubberband->setGeometry(m_curRectangle);
emit mouseMoved(m_curRectangle);
m_s_inEdit = false;
}
QGraphicsPixmapItem *LtsPictureView::getImage()
{
if (!m_image)
m_image = new QGraphicsPixmapItem;
return m_image;
}
+66
View File
@@ -0,0 +1,66 @@
/**
* @RDE_HEADER@
* Crop pictures
* $Id$
* $HeadURL$
* $Author$
* Created by Raphael Becker
*/
#pragma once
#include "lts_widgets_export.h"
#include <QGraphicsView>
class QRubberBand;
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsPictureView : public QGraphicsView
{
Q_OBJECT
public:
explicit LtsPictureView(QWidget *parent = nullptr);
~LtsPictureView();
void hideRubberband();
void newRubberBand();
void cutPicture();
QGraphicsPixmapItem *getImage();
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
bool m_mouseActive;
bool m_moveable;
bool m_aspectRatioEnabled;
bool m_s_inEdit;
double m_aspectRatio;
QRect m_curRectangle;
QGraphicsScene *m_gsPicture;
QRubberBand *m_rubberband;
QGraphicsPixmapItem *m_image;
QGraphicsPixmapItem *m_origimage;
QPoint m_origin;
QPoint m_bandoffset;
public slots:
void setPicture(const QImage &image, const QPoint &pos, const bool flag);
void setAspectRatio(double aspectRatio);
void ratioClicked(bool enabled);
void leftChanged(int left);
void rightChanged(int right);
void topChanged(int top);
void bottomChanged(int bottom);
signals:
void mouseMoved(const QRect &);
void setLimits(const QSize &, bool);
};
}
+140
View File
@@ -0,0 +1,140 @@
/**
* @RDE_HEADER@
* A Button with an elliptic Pixmap and a menu icon when hovering. In the style of LtsStyle.
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-06-20
*/
#include "lts_profile_button.h"
#include <QPainter>
#include <QPainterPath>
#include <QPaintEvent>
#include <QBrush>
#include <QPen>
#include <QRect>
#include <QEvent>
using namespace LtsWidgets;
LtsProfileButton::LtsProfileButton(QWidget *parent)
: QWidget(parent)
, m_isHovered(false)
, m_menuEnabled(true) // enabled by default
{
QSizePolicy qsp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
qsp.setHeightForWidth(true);
setSizePolicy(qsp);
}
void LtsProfileButton::setPixmap(const QPixmap &pixmap)
{
if (pixmap.isNull()) // invalid / empty pixmap given, trying to replace it with one from lts_resources
m_pixmap = QPixmap(QStringLiteral("icon:no_pic.svg")); // when this fails, the pixmap is still invalid, so no checks needed
else
m_pixmap = pixmap;
update();
}
void LtsProfileButton::enterEvent(QEvent *event)
{
//qWarning() << "enter";
m_isHovered = true;
update();
QWidget::enterEvent(event);
}
void LtsProfileButton::leaveEvent(QEvent *event)
{
//qWarning() << "leave";
m_isHovered = false;
update();
QWidget::leaveEvent(event);
}
bool LtsProfileButton::event(QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress && m_menuEnabled)
{
emit signalClicked();
event->accept();
// m_isHovered = false; // unhovered on click
update();
return true;
}
return QWidget::event(event); // otherwise..
}
void LtsProfileButton::paintEvent(QPaintEvent* event) // TODO: colors from palette (style plugin) ?
{
QPainter p(this);
p.save();
int minSize;
QPointF center;
minSize = height() > width() ? width() : height();
center = QPointF(width() / 2.0, height() / 2.0);
// draw profile picture
p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
// QBrush brush = QColor(QStringLiteral("white")); // TODO: from palette
QPixmap pixmap(minSize, minSize);
pixmap.fill(Qt::white);
if (!m_pixmap.isNull())
{
if (!(m_isHovered && m_menuEnabled)) // draw if NOT hovered or draw when hovered but menu is disabled
{
pixmap = m_pixmap.scaled(minSize, minSize, Qt::KeepAspectRatioByExpanding);
}
}
// p.setBrush(Qt::white);
// p.setPen(Qt::NoPen);
QPainterPath path;
path.addEllipse(center,
minSize / 2.0 - 4,
minSize / 2.0 - 4);
p.setClipPath(path);
p.drawPixmap(center - QPointF(minSize/2.0,minSize/2.0),pixmap);
// draw frame
p.setPen(QPen(QColor("gray"), 2));
p.drawEllipse(center,
minSize / 2.0 - 4,
minSize / 2.0 - 4);
// draw menu icon
if (m_isHovered && m_menuEnabled)
{
p.setPen(QPen(QColor(QStringLiteral("gray")), (minSize / 48 * 2))); // respect height, width is unnecessary
double xc = size().width()/2.012;
double yc = size().height()/2.012;
double diff = minSize/2.012;
p.drawLine(QPointF(xc - (diff/2.01f), yc - (diff/3.01f)), QPointF(xc + (diff/2.02f), yc - (diff/3.02f)));
p.drawLine(QPointF(xc - (diff/2.01f), yc - 0.01f), QPointF(xc + (diff/2.02f), yc - 0));
p.drawLine(QPointF(xc - (diff/2.01f), yc + (diff/3.01f)), QPointF(xc + (diff/2.02f), yc + (diff/3.02f)));
}
event->accept();
p.restore();
}
+70
View File
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* A Button with an elliptic Pixmap and a menu icon when hovering. In the style of LtsStyle.
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-06-20
*/
#pragma once
#include "lts_widgets_export.h"
#include <QWidget>
namespace LtsWidgets
{
/**
* @brief The LtsProfileButton class provides a Button which has a round form, containing a picture. On hovering the picture turns into
* a "menu" icon to symbolize the clickability of the button.
* If no pixmap / picture is set, the picture-layer is button colored.
*/
class LTSWIDGETS_EXPORT LtsProfileButton : public QWidget
{
Q_OBJECT
Q_PROPERTY( QPixmap pixMap READ pixmap WRITE setPixmap )
Q_PROPERTY( bool menuEnabled READ menuEnabled WRITE setMenuEnabled )
public:
explicit LtsProfileButton(QWidget *parent = nullptr);
/**
* @brief setPixmap sets the picture of this button. If no pixmap / picture is set, the picture-layer is button colored.
* @param pixmap The picture to draw on the button.
*/
void setPixmap(const QPixmap &pixmap);
const QPixmap &pixmap() const { return m_pixmap; }
/**
* @brief setMenuEnabled changes the hover & click behaviour. If the menu is enabled, the profile button indicates a menu on hovering, and by clicking it, the signal 'signalClicked' is emitted (menu is enabled by default)
* @param enabled true if menu is enabled, false if not
*/
void setMenuEnabled(bool enabled = true) { m_menuEnabled = enabled; }
bool menuEnabled() const { return m_menuEnabled; }
protected:
void paintEvent(QPaintEvent *event);
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
bool event(QEvent *event);
private:
QPixmap m_pixmap;
bool m_isHovered;
bool m_menuEnabled;
signals:
/**
* @brief signalClicked is emitted when the button has been clicked.
*/
void signalClicked();
};
}
+154
View File
@@ -0,0 +1,154 @@
/**
* @RDE_HEADER@
* Helper Class, which manages a progress Dialog with two states.
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-06-20
*/
#include "lts_progress_dialog_helper.h"
#include "lts_frameless_dialog.h"
#include <QCoreApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QTimer>
namespace LtsWidgets
{
LtsProgressDialogHelper::LtsProgressDialogHelper(QWidget *parent)
: QObject(parent)
, m_waitDlg(nullptr) // save mem
, m_parentWidget(parent)
, m_textLabel(nullptr)
{}
LtsProgressDialogHelper::~LtsProgressDialogHelper()
{
delete m_waitDlg;
}
void LtsProgressDialogHelper::setCustomText(const QString &customText)
{
if (m_waitDlg == nullptr || m_textLabel == nullptr)
return;
if (customText.isEmpty())
m_textLabel->setText(tr("Waiting for server reply..."));
else
m_textLabel->setText(customText);
}
void LtsProgressDialogHelper::setParentWidget(QWidget *newParent)
{
m_parentWidget = newParent;
if (m_waitDlg)
{
m_waitDlg->setParent(newParent);
QTimer::singleShot(0, m_waitDlg.data(), &LtsFramelessDialog::onUpdateGeometry);
}
}
void LtsProgressDialogHelper::slotShowWaitForReplyWindow()
{
if (m_waitDlg)
m_waitDlg->deleteLater(); //delete m_waitDlg; // when there is already an instance
m_waitDlg = new LtsFramelessDialog(m_parentWidget, Behaviour::Modal);
QHBoxLayout *l = new QHBoxLayout(m_waitDlg);
QProgressBar *pb = new QProgressBar(m_waitDlg);
pb->setMaximum(0);
pb->setValue(-1);
l->addWidget(pb);
m_textLabel = new QLabel(m_waitDlg);
setCustomText();
l->addWidget(m_textLabel);
m_waitDlg->setLayout(l);
m_waitDlg->show();
m_waitDlg->raise(); // TODO: needed when StyOnTop is active ?
//QCoreApplication::processEvents(); // do not look glitched if dds hangs
}
void LtsProgressDialogHelper::slotStopWaitForReplyWindow(const ReplyResult &replyResult, const QString &replyString)
{
if (m_waitDlg == nullptr)
{
// TODO: debug/warning that there is no wait dlg..
return;
}
//qDeleteAll(m_waitDlg->children()); // delete the layout and it's children
m_waitDlg->deleteLater(); // the easiest way
m_waitDlg = nullptr;
// .. a new layout, telling the reply result
QString resultText = tr("Unknown result (%1)!").arg((int)replyResult);
switch (replyResult)
{
case ReplyResult::Close:
{
return; // say nothing, just close
}
case ReplyResult::Success:
{
resultText = tr("Operation was successful");
break;
}
case ReplyResult::Failure:
{
resultText = tr("Operation has failed!\nReason: '%1'").arg(replyString);
break;
}
case ReplyResult::TimeOut:
{
resultText = tr("Operation has timed out and could not be processed! Please try again later");
break;
}
}
m_waitDlg = new LtsFramelessDialog(m_parentWidget, Behaviour::Modal);
QVBoxLayout *newLayout = new QVBoxLayout(m_waitDlg);
newLayout->addWidget(new QLabel(resultText, m_waitDlg));
QPushButton *btn = new QPushButton(tr("OK"),m_waitDlg);
connect(btn, &QPushButton::clicked,
this, &LtsProgressDialogHelper::accept); // TODO: or close?
newLayout->addWidget(btn);
m_waitDlg->setLayout(newLayout);
m_waitDlg->show();
//QCoreApplication::processEvents(); // do not look glitched if dds hangs
}
void LtsProgressDialogHelper::accept()
{
m_waitDlg->accept();
m_waitDlg->deleteLater();
m_waitDlg = nullptr;
emit accepted();
}
} // namespace LtsWidgets
+82
View File
@@ -0,0 +1,82 @@
/**
* @RDE_HEADER@
* Helper Class, which manages a progress Dialog with two states.
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-06-20
*/
#pragma once
#include "lts_widgets_export.h"
#include <QObject>
#include <QPointer>
class QLabel;
class QWidget;
namespace LtsWidgets
{
class LtsFramelessDialog;
/**
* @brief The LtsProgressDialogHelper class is a helper class, which can be used to easily
* show a Dialog with eather a wait state (indefinite progress bar), or a default message,
* adjusted to the result type (see ReplyResult)
*/
class LTSWIDGETS_EXPORT LtsProgressDialogHelper : public QObject
{
Q_OBJECT
public:
/**
* @brief The ReplyResult enum describes the behaviour of the slotSopWaitForReplyWindow method.
*/
enum class ReplyResult
{
Close = 0, // say nothing, just close automatically
Success, // dialog with success message, has to be receipted
Failure, // dialog with failure message plus reply string, has to be receipted
TimeOut, // dialog with timeout message, has to be receipted
};
explicit LtsProgressDialogHelper(QWidget *parent = nullptr);
~LtsProgressDialogHelper();
bool isShown() const { return !!m_waitDlg; }
void setCustomText(const QString &customText = QString());
void setParentWidget(QWidget *newParent);
public slots:
/**
* @brief slotShowWaitForReplyWindow shows a centered wait dialog which does not allow the user to interact with the application and cannot be canceled.
*/
void slotShowWaitForReplyWindow(/*const QString &textToShow*/);
/**
* @brief slotStopWaitForReplyWindow closes the wait dialog. Nothing happens when there was no show wait dialog called before.
* @param replyResult The wait dialog is closed on ReplyResult::Close. A new centered, modal dialog is shown on ReplyResult::Failure/Success/Timeout with an error message in it.
* @param replyString An explanatory string shown on ReplyResult::Failure to tell the user what went wrong on.
*/
void slotStopWaitForReplyWindow(const LtsWidgets::LtsProgressDialogHelper::ReplyResult &replyResult = ReplyResult::Close /* say nothing, just close */,
const QString &replyString = QString());
private:
QPointer<LtsFramelessDialog> m_waitDlg;
QWidget *m_parentWidget;
QPointer<QLabel> m_textLabel;
signals:
/**
* @brief accepted is emitted when accept() was called (e.g. by clicking okay)
*/
void accepted();
private slots:
void accept();
};
}
+848
View File
@@ -0,0 +1,848 @@
/**
* @RDE_HEADER@
* A slider that has a minimum and a maximum value, thus can define a range within a range.
* That could be a a snippet out of a video, a time range within an exercise...
* $Id$
* $HeadURL$
* $Author$
*
* Created by Malte Diers, NLE, 2020-01-06
*/
/*=========================================================================
Library: CTK
Copyright (c) Kitware Inc.
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.txt
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.
=========================================================================*/
#include "lts_range_slider.h"
// Qt includes
#include <QDebug>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QStyleOptionSlider>
#include <QStylePainter>
#include <QStyle>
#include <QToolTip>
namespace LtsWidgets
{
class LtsRangeSliderPrivate
{
Q_DECLARE_PUBLIC(LtsRangeSlider)
protected:
LtsRangeSlider *const q_ptr;
public:
/// Boolean indicates the selected handle
/// True for the minimum range handle, false for the maximum range handle
enum Handle
{
NoHandle = 0x0,
MinimumHandle = 0x1,
MaximumHandle = 0x2
};
Q_DECLARE_FLAGS(Handles, Handle)
explicit LtsRangeSliderPrivate(LtsRangeSlider &object);
void init();
/// Return the handle at the given pos, or none if no handle is at the pos.
/// If a handle is selected, handleRect is set to the handle rect.
/// otherwise return NoHandle and handleRect is set to the combined rect of
/// the min and max handles
Handle handleAtPos(const QPoint &pos, QRect &handleRect) const;
/// Copied verbatim from QSliderPrivate class (see QSlider.cpp)
int pixelPosToRangeValue(int pos) const;
int pixelPosFromRangeValue(int val) const;
/// Draw the bottom and top sliders.
void drawMinimumSlider(QStylePainter *painter) const;
void drawMaximumSlider(QStylePainter *painter) const;
/// End points of the range on the Model
int m_minimumValue = 0;
int m_maximumValue = 100;
/// End points of the range on the GUI. This is synced with the model.
int m_minimumPosition = 0;
int m_maximumPosition = 100;
/// Controls selected ?
QStyle::SubControl m_minimumSliderSelected = QStyle::SC_None;
QStyle::SubControl m_maximumSliderSelected = QStyle::SC_None;
/// See QSliderPrivate::clickOffset.
/// Overrides this ivar
int m_subclassClickOffset = 0;
/// See QSliderPrivate::position
/// Overrides this ivar.
int m_subclassPosition = 0;
/// Original width between the 2 bounds before any moves
float m_subclassWidth = 0;
LtsRangeSliderPrivate::Handles m_selectedHandles = {};
/// When symmetricMoves is true, moving a handle will move the other handle
/// symmetrically, otherwise the handles are independent.
bool m_symmetricMoves = false;
QString m_handleToolTip;
private:
Q_DISABLE_COPY(LtsRangeSliderPrivate)
};
// --------------------------------------------------------------------------
LtsRangeSliderPrivate::LtsRangeSliderPrivate(LtsRangeSlider &object)
: q_ptr(&object)
{}
// --------------------------------------------------------------------------
void LtsRangeSliderPrivate::init()
{
Q_Q(LtsRangeSlider);
m_minimumValue = q->minimum();
m_maximumValue = q->maximum();
m_minimumPosition = q->minimum();
m_maximumPosition = q->maximum();
q->connect(q, &LtsRangeSlider::rangeChanged, q, &LtsRangeSlider::onRangeChanged);
}
// --------------------------------------------------------------------------
LtsRangeSliderPrivate::Handle LtsRangeSliderPrivate::handleAtPos(const QPoint &pos, QRect &handleRect)const
{
Q_Q(const LtsRangeSlider);
QStyleOptionSlider option;
q->initStyleOption(&option);
// The functinos hitTestComplexControl only know about 1 handle. As we have
// 2, we change the position of the handle and test if the pos correspond to
// any of the 2 positions.
// Test the MinimumHandle
option.sliderPosition = m_minimumPosition;
option.sliderValue = m_minimumValue;
QStyle::SubControl minimumControl = q->style()->hitTestComplexControl(
QStyle::CC_Slider, &option, pos, q);
QRect minimumHandleRect = q->style()->subControlRect(
QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
// Test if the pos is under the Maximum handle
option.sliderPosition = m_maximumPosition;
option.sliderValue = m_maximumValue;
QStyle::SubControl maximumControl = q->style()->hitTestComplexControl(QStyle::CC_Slider, &option, pos, q);
QRect maximumHandleRect = q->style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, q);
// The pos is above both handles, select the closest handle
if (minimumControl == QStyle::SC_SliderHandle &&
maximumControl == QStyle::SC_SliderHandle)
{
int minDist = 0;
int maxDist = 0;
if (q->orientation() == Qt::Horizontal)
{
minDist = pos.x() - minimumHandleRect.left();
maxDist = maximumHandleRect.right() - pos.x();
}
else //if (q->orientation() == Qt::Vertical)
{
minDist = minimumHandleRect.bottom() - pos.y();
maxDist = pos.y() - maximumHandleRect.top();
}
Q_ASSERT(minDist >= 0 && maxDist >= 0);
minimumControl = minDist < maxDist ? minimumControl : QStyle::SC_None;
}
if (maximumControl == QStyle::SC_SliderHandle)
{
handleRect = maximumHandleRect;
return MaximumHandle;
}
else if (minimumControl == QStyle::SC_SliderHandle)
{
handleRect = minimumHandleRect;
return MinimumHandle;
}
handleRect = minimumHandleRect.united(maximumHandleRect);
return NoHandle;
}
// --------------------------------------------------------------------------
// Copied verbatim from QSliderPrivate::pixelPosToRangeValue. See QSlider.cpp
//
int LtsRangeSliderPrivate::pixelPosToRangeValue(int pos) const
{
Q_Q(const LtsRangeSlider);
QStyleOptionSlider option;
q->initStyleOption(&option);
QRect gr = q->style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderGroove,
q);
QRect sr = q->style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderHandle,
q);
int sliderMin, sliderMax, sliderLength;
if (option.orientation == Qt::Horizontal)
{
sliderLength = sr.width();
sliderMin = gr.x();
sliderMax = gr.right() - sliderLength + 1;
}
else
{
sliderLength = sr.height();
sliderMin = gr.y();
sliderMax = gr.bottom() - sliderLength + 1;
}
return QStyle::sliderValueFromPosition(q->minimum(),
q->maximum(),
pos - sliderMin,
sliderMax - sliderMin,
option.upsideDown);
}
//---------------------------------------------------------------------------
int LtsRangeSliderPrivate::pixelPosFromRangeValue(int val) const
{
Q_Q(const LtsRangeSlider);
QStyleOptionSlider option;
q->initStyleOption(&option);
QRect gr = q->style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderGroove,
q);
QRect sr = q->style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderHandle,
q);
int sliderMin, sliderMax, sliderLength;
if (option.orientation == Qt::Horizontal)
{
sliderLength = sr.width();
sliderMin = gr.x();
sliderMax = gr.right() - sliderLength + 1;
}
else
{
sliderLength = sr.height();
sliderMin = gr.y();
sliderMax = gr.bottom() - sliderLength + 1;
}
return QStyle::sliderPositionFromValue(q->minimum(),
q->maximum(),
val,
sliderMax - sliderMin,
option.upsideDown) + sliderMin;
}
//---------------------------------------------------------------------------
// Draw slider at the bottom end of the range
void LtsRangeSliderPrivate::drawMinimumSlider(QStylePainter *painter) const
{
Q_Q(const LtsRangeSlider);
QStyleOptionSlider option;
q->initMinimumSliderStyleOption(&option);
option.subControls = QStyle::SC_SliderHandle;
option.sliderValue = m_minimumValue;
option.sliderPosition = m_minimumPosition;
if (q->isMinimumSliderDown())
{
option.activeSubControls = QStyle::SC_SliderHandle;
option.state |= QStyle::State_Sunken;
}
#ifdef Q_OS_MAC
// On mac style, drawing just the handle actually draws also the groove.
QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
QStyle::SC_SliderHandle, q);
painter->setClipRect(clip);
#endif
painter->drawComplexControl(QStyle::CC_Slider, option);
}
//---------------------------------------------------------------------------
// Draw slider at the top end of the range
void LtsRangeSliderPrivate::drawMaximumSlider(QStylePainter *painter) const
{
Q_Q(const LtsRangeSlider);
QStyleOptionSlider option;
q->initMaximumSliderStyleOption(&option);
option.subControls = QStyle::SC_SliderHandle;
option.sliderValue = m_maximumValue;
option.sliderPosition = m_maximumPosition;
if (q->isMaximumSliderDown())
{
option.activeSubControls = QStyle::SC_SliderHandle;
option.state |= QStyle::State_Sunken;
}
#ifdef Q_OS_MAC
// On mac style, drawing just the handle actually draws also the groove.
QRect clip = q->style()->subControlRect(QStyle::CC_Slider, &option,
QStyle::SC_SliderHandle, q);
painter->setClipRect(clip);
#endif
painter->drawComplexControl(QStyle::CC_Slider, option);
}
// --------------------------------------------------------------------------
LtsRangeSlider::LtsRangeSlider(Qt::Orientation o,
QWidget *parent)
: QSlider(o, parent)
, d_ptr(new LtsRangeSliderPrivate(*this))
{
Q_D(LtsRangeSlider);
d->init();
}
// --------------------------------------------------------------------------
LtsRangeSlider::LtsRangeSlider(LtsRangeSliderPrivate *impl, Qt::Orientation o,
QWidget *parent)
: QSlider(o, parent)
, d_ptr(impl)
{
Q_D(LtsRangeSlider);
d->init();
}
// --------------------------------------------------------------------------
LtsRangeSlider::~LtsRangeSlider()
{
}
// --------------------------------------------------------------------------
int LtsRangeSlider::minimumValue() const
{
Q_D(const LtsRangeSlider);
return d->m_minimumValue;
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setMinimumValue(int min)
{
Q_D(LtsRangeSlider);
setValues(min, qMax(d->m_maximumValue, min));
}
// --------------------------------------------------------------------------
int LtsRangeSlider::maximumValue() const
{
Q_D(const LtsRangeSlider);
return d->m_maximumValue;
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setMaximumValue(int max)
{
Q_D(LtsRangeSlider);
setValues(qMin(d->m_minimumValue, max), max);
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setValues(int min, int max)
{
Q_D(LtsRangeSlider);
const int minValue = qBound(minimum(), qMin(min, max), maximum());
const int maxValue = qBound(minimum(), qMax(min, max), maximum());
bool emitMinValChanged = (minValue != d->m_minimumValue);
bool emitMaxValChanged = (maxValue != d->m_maximumValue);
d->m_minimumValue = minValue;
d->m_maximumValue = maxValue;
bool emitMinPosChanged = (minValue != d->m_minimumPosition);
bool emitMaxPosChanged = (maxValue != d->m_maximumPosition);
d->m_minimumPosition = minValue;
d->m_maximumPosition = maxValue;
if (isSliderDown())
{
if (emitMinPosChanged || emitMaxPosChanged)
emit positionsChanged(d->m_minimumPosition, d->m_maximumPosition);
if (emitMinPosChanged)
emit minimumPositionChanged(d->m_minimumPosition);
if (emitMaxPosChanged)
emit maximumPositionChanged(d->m_maximumPosition);
}
if (emitMinValChanged || emitMaxValChanged)
emit valuesChanged(d->m_minimumValue, d->m_maximumValue);
if (emitMinValChanged)
emit minimumValueChanged(d->m_minimumValue);
if (emitMaxValChanged)
emit maximumValueChanged(d->m_maximumValue);
if (emitMinPosChanged || emitMaxPosChanged ||
emitMinValChanged || emitMaxValChanged)
update();
}
// --------------------------------------------------------------------------
int LtsRangeSlider::minimumPosition() const
{
Q_D(const LtsRangeSlider);
return d->m_minimumPosition;
}
// --------------------------------------------------------------------------
int LtsRangeSlider::maximumPosition() const
{
Q_D(const LtsRangeSlider);
return d->m_maximumPosition;
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setMinimumPosition(int min)
{
Q_D(const LtsRangeSlider);
setPositions(min, qMax(min, d->m_maximumPosition));
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setMaximumPosition(int max)
{
Q_D(const LtsRangeSlider);
setPositions(qMin(d->m_minimumPosition, max), max);
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setPositions(int min, int max)
{
Q_D(LtsRangeSlider);
const int minPosition = qBound(minimum(), qMin(min, max), maximum());
const int maxPosition = qBound(minimum(), qMax(min, max), maximum());
bool emitMinPosChanged = (minPosition != d->m_minimumPosition);
bool emitMaxPosChanged = (maxPosition != d->m_maximumPosition);
if (!emitMinPosChanged && !emitMaxPosChanged)
return;
d->m_minimumPosition = minPosition;
d->m_maximumPosition = maxPosition;
if (!hasTracking())
update();
if (isSliderDown())
{
if (emitMinPosChanged)
emit minimumPositionChanged(d->m_minimumPosition);
if (emitMaxPosChanged)
emit maximumPositionChanged(d->m_maximumPosition);
if (emitMinPosChanged || emitMaxPosChanged)
emit positionsChanged(d->m_minimumPosition, d->m_maximumPosition);
}
if (hasTracking())
{
triggerAction(SliderMove);
setValues(d->m_minimumPosition, d->m_maximumPosition);
}
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setSymmetricMoves(bool symmetry)
{
Q_D(LtsRangeSlider);
d->m_symmetricMoves = symmetry;
}
// --------------------------------------------------------------------------
bool LtsRangeSlider::symmetricMoves() const
{
Q_D(const LtsRangeSlider);
return d->m_symmetricMoves;
}
// --------------------------------------------------------------------------
void LtsRangeSlider::onRangeChanged(int minimum, int maximum)
{
Q_UNUSED(minimum);
Q_UNUSED(maximum);
Q_D(LtsRangeSlider);
setValues(d->m_minimumValue, d->m_maximumValue);
}
// --------------------------------------------------------------------------
// Render
void LtsRangeSlider::paintEvent(QPaintEvent *)
{
Q_D(LtsRangeSlider);
QStyleOptionSlider option;
initStyleOption(&option);
QStylePainter painter(this);
// -------------------------------------------------------------------------
// Render complete range
//
painter.setPen(Qt::lightGray);
if (option.orientation == Qt::Horizontal)
{
painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom());
painter.drawRect(option.rect.left(), option.rect.center().y() - 1, option.rect.right() - option.rect.left(), 3);
painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom());
}
else
{
painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top());
painter.drawRect(option.rect.center().x() - 1, option.rect.top(), 3, option.rect.top() - option.rect.bottom());
painter.drawLine(option.rect.center().x(), option.rect.top(), option.rect.center().x(), option.rect.bottom());
painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom());
}
// -------------------------------------------------------------------------
option.subControls = QStyle::SC_SliderGroove;
// Move to minimum to not highlight the SliderGroove.
// On mac style, drawing just the slider groove also draws the handles,
// therefore we give a negative (outside of view) position.
option.sliderValue = minimum() - maximum();
option.sliderPosition = minimum() - maximum();
option.subControls &= ~QStyle::SC_SliderGroove;
painter.drawComplexControl(QStyle::CC_Slider, option);
option.subControls |= QStyle::SC_SliderGroove;
option.sliderPosition = d->m_minimumPosition;
const QRect lr = style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderHandle,
this);
option.sliderPosition = d->m_maximumPosition;
const QRect ur = style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderHandle,
this);
QRect sr = style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderGroove,
this);
QRect rangeBox;
if (option.orientation == Qt::Horizontal)
{
rangeBox = QRect(QPoint(qMin(lr.center().x(), ur.center().x()), sr.center().y() - 2),
QPoint(qMax(lr.center().x(), ur.center().x()), sr.center().y() + 1));
}
else
{
rangeBox = QRect(QPoint(sr.center().x() - 2, qMin(lr.center().y(), ur.center().y())),
QPoint(sr.center().x() + 1, qMax(lr.center().y(), ur.center().y())));
}
// -----------------------------
// Render the range
//
QRect groove = style()->subControlRect(QStyle::CC_Slider,
&option,
QStyle::SC_SliderGroove,
this);
groove.adjust(0, 0, -1, 0);
// Create default colors based on the transfer function.
//
QColor highlight = palette().color(QPalette::Normal, QPalette::Highlight);
QLinearGradient gradient;
if (option.orientation == Qt::Horizontal)
gradient = QLinearGradient(groove.center().x(), groove.top(), groove.center().x(), groove.bottom());
else
gradient = QLinearGradient(groove.left(), groove.center().y(), groove.right(), groove.center().y());
// TODO: Set this based on the supplied transfer function
//QColor l = Qt::darkGray;
//QColor u = Qt::black;
gradient.setColorAt(0, highlight.darker(120));
gradient.setColorAt(1, highlight.lighter(160));
painter.setPen(QPen(highlight.darker(150), 0));
painter.setBrush(gradient);
painter.drawRect(rangeBox.intersected(groove));
// -----------------------------------
// Render the sliders
//
if (isMinimumSliderDown())
{
painter.setClipRect(ur);
d->drawMaximumSlider(&painter);
painter.setClipRect(lr);
d->drawMinimumSlider(&painter);
}
else
{
painter.setClipRect(lr);
d->drawMinimumSlider(&painter);
painter.setClipRect(ur);
d->drawMaximumSlider(&painter);
}
}
// --------------------------------------------------------------------------
// Standard Qt UI events
void LtsRangeSlider::mousePressEvent(QMouseEvent *ev)
{
Q_D(LtsRangeSlider);
if (minimum() == maximum() || (ev->buttons() ^ ev->button()))
{
ev->ignore();
return;
}
int mepos = orientation() == Qt::Horizontal ? ev->pos().x() : ev->pos().y();
QStyleOptionSlider option;
initStyleOption(&option);
QRect handleRect;
LtsRangeSliderPrivate::Handle handle_ = d->handleAtPos(ev->pos(), handleRect);
if (handle_ != LtsRangeSliderPrivate::NoHandle)
{
if (ev->buttons() == Qt::RightButton)
{
d->m_subclassPosition = (d->m_minimumPosition + d->m_maximumPosition) / 2.;
d->m_subclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_subclassPosition);
d->m_subclassWidth = (d->m_maximumPosition - d->m_minimumPosition) / 2.;
//qMax(d->m_subclassPosition - d->m_minimumPosition, d->m_maximumPosition - d->m_subclassPosition);
setSliderDown(true);
if (!isMinimumSliderDown() || !isMaximumSliderDown())
{
d->m_selectedHandles = QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MinimumHandle) |
QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MaximumHandle);
QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
update(handleRect.united(sr));
}
ev->accept();
return;
}
d->m_subclassPosition = (handle_ == LtsRangeSliderPrivate::MinimumHandle) ? d->m_minimumPosition : d->m_maximumPosition;
// save the position of the mouse inside the handle for later
d->m_subclassClickOffset = mepos - (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top());
setSliderDown(true);
if (d->m_selectedHandles != handle_)
{
d->m_selectedHandles = handle_;
update(handleRect);
}
// Accept the mouseEvent
ev->accept();
return;
}
// if we are here, no handles have been pressed
// Check if we pressed on the groove between the 2 handles
QStyle::SubControl control = style()->hitTestComplexControl(QStyle::CC_Slider, &option, ev->pos(), this);
QRect sr = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
int minCenter = (orientation() == Qt::Horizontal ? handleRect.left() : handleRect.top());
int maxCenter = (orientation() == Qt::Horizontal ? handleRect.right() : handleRect.bottom());
if (control == QStyle::SC_SliderGroove && mepos > minCenter && mepos < maxCenter)
{
// warning lost of precision it might be fatal
d->m_subclassPosition = (d->m_minimumPosition + d->m_maximumPosition) / 2.;
d->m_subclassClickOffset = mepos - d->pixelPosFromRangeValue(d->m_subclassPosition);
d->m_subclassWidth = (d->m_maximumPosition - d->m_minimumPosition) / 2.;
//qMax(d->m_subclassPosition - d->m_minimumPosition, d->m_maximumPosition - d->m_subclassPosition);
setSliderDown(true);
if (!isMinimumSliderDown() || !isMaximumSliderDown())
{
d->m_selectedHandles = QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MinimumHandle) |
QFlags<LtsRangeSliderPrivate::Handle>(LtsRangeSliderPrivate::MaximumHandle);
update(handleRect.united(sr));
}
ev->accept();
return;
}
ev->ignore();
}
// --------------------------------------------------------------------------
// Standard Qt UI events
void LtsRangeSlider::mouseMoveEvent(QMouseEvent *ev)
{
Q_D(LtsRangeSlider);
if (!d->m_selectedHandles)
{
ev->ignore();
return;
}
int mepos = orientation() == Qt::Horizontal ? ev->pos().x() : ev->pos().y();
QStyleOptionSlider option;
initStyleOption(&option);
// Both handles are down (the user clicked in between the handles)
if (isMinimumSliderDown() && isMaximumSliderDown())
{
int newPosition = d->pixelPosToRangeValue(mepos);
int newMinPosition = newPosition - static_cast<int>(d->m_subclassWidth);
int newMaxPosition = newPosition + static_cast<int>(d->m_subclassWidth + .5);
// prevent the range from becoming smaller
if (newMinPosition < minimum())
{
newMinPosition = minimum();
newMaxPosition = newMinPosition + (d->m_maximumPosition - d->m_minimumPosition);
}
else if (newMaxPosition > maximum())
{
newMaxPosition = maximum();
newMinPosition = newMaxPosition - (d->m_maximumPosition - d->m_minimumPosition);
}
setPositions(newMinPosition, newMaxPosition);
ev->accept();
return;
}
const int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &option, this);
int newPosition = d->pixelPosToRangeValue(mepos - d->m_subclassClickOffset);
if (m >= 0)
{
const QRect r = rect().adjusted(-m, -m, m, m);
if (!r.contains(ev->pos()))
newPosition = d->m_subclassPosition;
}
// Only the lower/left slider is down
if (isMinimumSliderDown() && !isMaximumSliderDown())
{
double newMinPos = qMin(newPosition, d->m_maximumPosition);
setPositions(newMinPos, d->m_maximumPosition + (d->m_symmetricMoves ? d->m_minimumPosition - newMinPos : 0));
}
// Only the upper/right slider is down
else if (isMaximumSliderDown() && !isMinimumSliderDown())
{
double newMaxPos = qMax(d->m_minimumPosition, newPosition);
setPositions(d->m_minimumPosition - (d->m_symmetricMoves ? newMaxPos - d->m_maximumPosition : 0), newMaxPos);
}
ev->accept();
}
// --------------------------------------------------------------------------
// Standard Qt UI mouseEvents
void LtsRangeSlider::mouseReleaseEvent(QMouseEvent *ev)
{
Q_D(LtsRangeSlider);
QSlider::mouseReleaseEvent(ev);
setSliderDown(false);
d->m_selectedHandles = {};
update();
}
// --------------------------------------------------------------------------
bool LtsRangeSlider::isMinimumSliderDown() const
{
Q_D(const LtsRangeSlider);
return d->m_selectedHandles & LtsRangeSliderPrivate::MinimumHandle;
}
// --------------------------------------------------------------------------
bool LtsRangeSlider::isMaximumSliderDown() const
{
Q_D(const LtsRangeSlider);
return d->m_selectedHandles & LtsRangeSliderPrivate::MaximumHandle;
}
// --------------------------------------------------------------------------
void LtsRangeSlider::initMinimumSliderStyleOption(QStyleOptionSlider *option) const
{
initStyleOption(option);
}
// --------------------------------------------------------------------------
void LtsRangeSlider::initMaximumSliderStyleOption(QStyleOptionSlider *option) const
{
initStyleOption(option);
}
// --------------------------------------------------------------------------
QString LtsRangeSlider::handleToolTip()const
{
Q_D(const LtsRangeSlider);
return d->m_handleToolTip;
}
// --------------------------------------------------------------------------
void LtsRangeSlider::setHandleToolTip(const QString &toolTip)
{
Q_D(LtsRangeSlider);
d->m_handleToolTip = toolTip;
}
// --------------------------------------------------------------------------
bool LtsRangeSlider::event(QEvent *event)
{
Q_D(LtsRangeSlider);
switch (event->type())
{
case QEvent::ToolTip:
{
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
QStyleOptionSlider opt;
// Test the MinimumHandle
opt.sliderPosition = d->m_minimumPosition;
opt.sliderValue = d->m_minimumValue;
initStyleOption(&opt);
QStyle::SubControl hoveredControl = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, helpEvent->pos(), this);
if (!d->m_handleToolTip.isEmpty() && hoveredControl == QStyle::SC_SliderHandle)
{
QToolTip::showText(helpEvent->globalPos(), d->m_handleToolTip.arg(minimumValue()));
event->accept();
return true;
}
// Test the MaximumHandle
opt.sliderPosition = d->m_maximumPosition;
opt.sliderValue = d->m_maximumValue;
initStyleOption(&opt);
hoveredControl = style()->hitTestComplexControl(QStyle::CC_Slider, &opt, helpEvent->pos(), this);
if (!d->m_handleToolTip.isEmpty() && hoveredControl == QStyle::SC_SliderHandle)
{
QToolTip::showText(helpEvent->globalPos(), d->m_handleToolTip.arg(maximumValue()));
event->accept();
return true;
}
}
default:
break;
}
return QSlider::event(event);
}
} // namespace
+219
View File
@@ -0,0 +1,219 @@
/**
* @RDE_HEADER@
* A slider that has a minimum and a maximum value, thus can define a range within a range.
* That could be a a snippet out of a video, a time range within an exercise...
* $Id$
* $HeadURL$
* $Author$
*
* Created by Malte Diers, NLE, 2020-01-06
*/
/*=========================================================================
Library: CTK
Copyright (c) Kitware Inc.
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.txt
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.
=========================================================================*/
#pragma once
#include "lts_widgets_export.h"
// Qt includes
#include <QSlider>
namespace LtsWidgets
{
class LtsRangeSliderPrivate;
/// \ingroup Widgets
///
/// A ctkRangeSlider is a slider that lets you input 2 values instead of one
/// (see QSlider). These values are typically a lower and upper bound.
/// Values are comprised between the range of the slider. See setRange(),
/// minimum() and maximum(). The upper bound can't be smaller than the
/// lower bound and vice-versa.
/// When setting new values (setMinimumValue(), setMaximumValue() or
/// setValues()), make sure they lie between the range (minimum(), maximum())
/// of the slider, they would be forced otherwised. If it is not the behavior
/// you desire, you can set the range first (setRange(), setMinimum(),
/// setMaximum())
/// TODO: support triggerAction(QAbstractSlider::SliderSingleStepSub) that
/// moves both values at a time.
/// \sa ctkDoubleRangeSlider, ctkDoubleSlider, ctkRangeWidget
class LTSWIDGETS_EXPORT LtsRangeSlider : public QSlider
{
Q_OBJECT
Q_PROPERTY(int minimumValue READ minimumValue WRITE setMinimumValue)
Q_PROPERTY(int maximumValue READ maximumValue WRITE setMaximumValue)
Q_PROPERTY(int minimumPosition READ minimumPosition WRITE setMinimumPosition)
Q_PROPERTY(int maximumPosition READ maximumPosition WRITE setMaximumPosition)
Q_PROPERTY(bool symmetricMoves READ symmetricMoves WRITE setSymmetricMoves)
Q_PROPERTY(QString handleToolTip READ handleToolTip WRITE setHandleToolTip)
public:
/// Constructor, builds a ctkRangeSlider that ranges from 0 to 100 and has
/// a lower and upper values of 0 and 100 respectively, other properties
/// are set the QSlider default properties.
explicit LtsRangeSlider(Qt::Orientation o, QWidget *parent = nullptr);
explicit LtsRangeSlider(QWidget *parent = nullptr)
: LtsRangeSlider(Qt::Vertical, parent)
{}
~LtsRangeSlider();
///
/// This property holds the slider's current minimum value.
/// The slider silently forces minimumValue to be within the legal range:
/// minimum() <= minimumValue() <= maximumValue() <= maximum().
/// Changing the minimumValue also changes the minimumPosition.
int minimumValue() const;
///
/// This property holds the slider's current maximum value.
/// The slider forces the maximum value to be within the legal range:
/// The slider silently forces maximumValue to be within the legal range:
/// Changing the maximumValue also changes the maximumPosition.
int maximumValue() const;
///
/// This property holds the current slider minimum position.
/// If tracking is enabled (the default), this is identical to minimumValue.
int minimumPosition() const;
void setMinimumPosition(int min);
///
/// This property holds the current slider maximum position.
/// If tracking is enabled (the default), this is identical to maximumValue.
int maximumPosition() const;
void setMaximumPosition(int max);
///
/// Utility function that set the minimum position and
/// maximum position at once.
void setPositions(int min, int max);
///
/// When symmetricMoves is true, moving a handle will move the other handle
/// symmetrically, otherwise the handles are independent. False by default
bool symmetricMoves() const;
void setSymmetricMoves(bool symmetry);
///
/// Controls the text to display for the handle tooltip. It is in addition
/// to the widget tooltip.
/// "%1" is replaced by the current value of the slider.
/// Empty string (by default) means no tooltip.
QString handleToolTip() const;
void setHandleToolTip(const QString &toolTip);
/// Returns true if the minimum value handle is down, false if it is up.
/// \sa isMaximumSliderDown()
bool isMinimumSliderDown() const;
/// Returns true if the maximum value handle is down, false if it is up.
/// \sa isMinimumSliderDown()
bool isMaximumSliderDown() const;
Q_SIGNALS:
///
/// This signal is emitted when the slider minimum value has changed,
/// with the new slider value as argument.
void minimumValueChanged(int min);
///
/// This signal is emitted when the slider maximum value has changed,
/// with the new slider value as argument.
void maximumValueChanged(int max);
///
/// Utility signal that is fired when minimum or maximum values have changed.
void valuesChanged(int min, int max);
///
/// This signal is emitted when sliderDown is true and the slider moves.
/// This usually happens when the user is dragging the minimum slider.
/// The value is the new slider minimum position.
/// This signal is emitted even when tracking is turned off.
void minimumPositionChanged(int min);
///
/// This signal is emitted when sliderDown is true and the slider moves.
/// This usually happens when the user is dragging the maximum slider.
/// The value is the new slider maximum position.
/// This signal is emitted even when tracking is turned off.
void maximumPositionChanged(int max);
///
/// Utility signal that is fired when minimum or maximum positions
/// have changed.
void positionsChanged(int min, int max);
public Q_SLOTS:
///
/// This property holds the slider's current minimum value.
/// The slider silently forces min to be within the legal range:
/// minimum() <= min <= maximumValue() <= maximum().
/// Note: Changing the minimumValue also changes the minimumPosition.
/// \sa stMaximumValue, setValues, setMinimum, setMaximum, setRange
void setMinimumValue(int min);
///
/// This property holds the slider's current maximum value.
/// The slider silently forces max to be within the legal range:
/// minimum() <= minimumValue() <= max <= maximum().
/// Note: Changing the maximumValue also changes the maximumPosition.
/// \sa stMinimumValue, setValues, setMinimum, setMaximum, setRange
void setMaximumValue(int max);
///
/// Utility function that set the minimum value and maximum value at once.
/// The slider silently forces min and max to be within the legal range:
/// minimum() <= min <= max <= maximum().
/// Note: Changing the minimumValue and maximumValue also changes the
/// minimumPosition and maximumPosition.
/// \sa setMinimumValue, setMaximumValue, setMinimum, setMaximum, setRange
void setValues(int min, int max);
protected Q_SLOTS:
void onRangeChanged(int minimum, int maximum);
protected:
LtsRangeSlider(LtsRangeSliderPrivate *impl, Qt::Orientation o, QWidget *parent = nullptr);
// Description:
// Standard Qt UI events
void mousePressEvent(QMouseEvent *ev) override;
void mouseMoveEvent(QMouseEvent *ev) override;
void mouseReleaseEvent(QMouseEvent *ev) override;
// Description:
// Rendering is done here.
void paintEvent(QPaintEvent *ev) override;
void initMinimumSliderStyleOption(QStyleOptionSlider *option) const;
void initMaximumSliderStyleOption(QStyleOptionSlider *option) const;
// Description:
// Reimplemented for the tooltips
bool event(QEvent *event) override;
protected:
QScopedPointer<LtsRangeSliderPrivate> d_ptr;
private:
Q_DECLARE_PRIVATE(LtsRangeSlider)
Q_DISABLE_COPY(LtsRangeSlider)
};
} // namespace
+219
View File
@@ -0,0 +1,219 @@
/**
* @RDE_HEADER@
* ComboBox's popup, optimized for tablets (bigger dropdown/popup)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#include "lts_selection_popup.h"
#include "ui_lts_selection_popup.h"
#include "lts_base.h"
#include <QSortFilterProxyModel>
#include <QAbstractItemModel>
#include <QRadioButton>
#include <QScrollBar>
//#define NEVER
#ifdef NEVER
# include <QDebug>
#endif // NEVER
namespace LtsWidgets
{
class RadioButton : public QRadioButton
{
Q_OBJECT
public:
explicit RadioButton(int row, const QString &text)
: QRadioButton(text)
, m_row(row)
{}
int row() const { return m_row; }
signals:
void doubleClickEvent();
private:
int m_row;
void mouseDoubleClickEvent(QMouseEvent *) override
{
emit doubleClickEvent();
}
};
LtsSelectionPopup::LtsSelectionPopup(QAbstractItemModel* model,
int currentIndex,
QWidget *parent)
: LtsFramelessDialog(parent, Behaviour::Popup)
, m_ui(new Ui::LtsSelectionPopup) // TODO: morph .ui to QDialog (is QWidget)
, m_currentIndex(currentIndex)
, m_firstIndex(currentIndex)
, m_filter(new QSortFilterProxyModel(this))
, m_valid(false)
{
m_ui->setupUi(this); // i am sorry for this solution, but qlistview's are so unthankfully to work with
m_filter->setSourceModel(model);
m_filter->setFilterCaseSensitivity(Qt::CaseInsensitive);
slotFilterChanged();
#ifdef TABLET_MODE
m_ui->leSearch->setVisible(false);
m_ui->label->setVisible(false);
#endif
connect(m_ui->pbCancel, &QPushButton::clicked, this, &LtsSelectionPopup::reject);
connect(m_ui->pbSelect, &QPushButton::clicked, this, &LtsSelectionPopup::accept);
connect(m_ui->leSearch, &QLineEdit::textEdited,
this, &LtsSelectionPopup::slotFilterChanged);
#ifdef NEVER
// debugging :
// // highlighted -> UserInteraction
// connect(this, static_cast<void (LtsSelectionPopup::*)(int)>(&LtsSelectionPopup::highlighted), this, [](int idx){
// qCritical() << " called: highlighted(int)";
// });
// currentIndexChanged -> programmatically & UserInteraction
connect(this, static_cast<void (LtsSelectionPopup::*)(int)>(&LtsSelectionPopup::currentIndexChanged), this, [](int idx){
qCritical() << " LtsSelectionPopup: called: currentIndexChanged(int), idx=" << idx;
});
// activated -> UserInteraction
connect(this, static_cast<void (LtsSelectionPopup::*)(int)>(&LtsSelectionPopup::activated), this, [](int idx){
qCritical() << " LtsSelectionPopup: called: activated(int), idx=" << idx;
});
// : debugging
#endif // NEVER
}
LtsSelectionPopup::~LtsSelectionPopup()
{
delete m_ui;
}
void LtsSelectionPopup::slotFilterChanged()
{
m_filter->setFilterWildcard(m_ui->leSearch->text().count() ? m_ui->leSearch->text() : QStringLiteral("*") /* show everything if nothing was entered */);
refresh();
}
void LtsSelectionPopup::refresh()
{
m_valid = false; // invalidate, none selected as all deleted
qDeleteAll(m_ui->itemLayoutWidget->findChildren<RadioButton*>()); // delete all RBs
QLayoutItem *tem = nullptr;
if (m_ui->itemLayoutWidget->layout()->count() > 0)
{
tem = m_ui->itemLayoutWidget->layout()->takeAt(0);
}
int count = m_filter->rowCount();
for (int i = 0; i < count; i++)
{
int realRow = m_filter->mapToSource(m_filter->index(i, 0)).row();
RadioButton *rbItem = new RadioButton(realRow, m_filter->data(m_filter->index(i, 0)).toString());
int disable = m_filter->data(m_filter->index(i, 0), Qt::UserRole-1).toInt();
if (disable == 1)
rbItem->setEnabled(false);
m_ui->itemLayoutWidget->layout()->addWidget(rbItem);
if (realRow == m_currentIndex)
{
rbItem->setChecked(true);
m_valid = true;
}
connect(rbItem, &RadioButton::doubleClickEvent,
this, &LtsSelectionPopup::accept); // is always selected then..
connect(rbItem, &RadioButton::toggled,
this, [this](bool checked)
{
if (checked)
m_ui->pbSelect->setEnabled(true);
});
}
if (tem)
m_ui->itemLayoutWidget->layout()->addItem(tem);
if (count == 1) // if there is only one item, then allow the user to click Select (or pressing enter) to select the only item available
m_valid = true;
m_ui->pbSelect->setEnabled(m_valid);
}
void LtsSelectionPopup::showEvent(QShowEvent *event)
{
LtsFramelessDialog::showEvent(event);
m_ui->leSearch->setFocus();
resize(sizeHint().width() + m_ui->scrollArea->horizontalScrollBar()->height() , sizeHint().height());
}
void LtsSelectionPopup::selectItem(int row, bool bCurrentIndexChanged, bool bActivated)
{
// order of signals (like Qt 5.9): 1st: currentIndexChanged 2nd: activated
if (bCurrentIndexChanged)
emit currentIndexChanged(row);
if (bActivated)
emit activated(row);
m_ui->pbSelect->setEnabled(true);
LtsFramelessDialog::accept(); // only accept if one item is selected
}
void LtsSelectionPopup::accept()
{
const QList<RadioButton *> items = m_ui->itemLayoutWidget->findChildren<RadioButton *>();
int count = items.count();
for (int i = 0; i < count; i++)
{
const RadioButton *rb = items.at(i);
if (rb->isChecked())
{
int realRow = m_filter->mapToSource(m_filter->index(i, 0)).row();
bool bCurrentIndexChanged = realRow != m_firstIndex; // currentIndex has changed?
bool bActivated = true;
selectItem(realRow, bCurrentIndexChanged, bActivated); // & closes dialog
return; // no need to go further
}
}
// okay, nothing was selected, is there only one item available? if yes, select it and accept
if (count == 1)
{
const RadioButton *rb = items.at(0);
selectItem(rb->row(), true, true); // item was not selected before, so it must also be emitted as "activated" // & closes dialog
return; // no need to go further
}
// none selected and multiple choises? ignore..
}
} // namespace LtsWidgets
#include "lts_selection_popup.moc"
+112
View File
@@ -0,0 +1,112 @@
/**
* @RDE_HEADER@
* ComboBox's popup, optimized for tablets (bigger dropdown/popup)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#pragma once
#include "lts_frameless_dialog.h"
#include "lts_widgets_export.h"
namespace Ui
{
class LtsSelectionPopup;
}
class QAbstractItemModel;
class QSortFilterProxyModel;
namespace LtsWidgets
{
/**
* @brief The LtsSelectionPopup is a popup window (LtsFramelessDialog) with a list of all QRadioButtons from a model with a search/filter edit.
* (former 'LtsComboBoxPopup' class WAS an internal Popup class for the LtsComboBox and is not meant for export)
* This is a custom Popup which is touch-friendly and screen-centered.
*/
class LTSWIDGETS_EXPORT LtsSelectionPopup : public LtsFramelessDialog
{
Q_OBJECT
public:
/**
* @brief LtsSelectionPopup creates a popup with all its items (from model) in it
* @param model the model which contains all items
* @param currentIndex which item is currently selected (to not select any item: a value smaller than 0)
* @param parent parent of this widget (not the LtsComboBox, the actual parent)
*/
LtsSelectionPopup(QAbstractItemModel *model,
int currentIndex,
QWidget *parent = nullptr);
~LtsSelectionPopup();
protected:
/**
* @brief showEvent selects the search/filter widget on show
* @param event
*/
void showEvent(QShowEvent *event) override;
private:
Ui::LtsSelectionPopup *m_ui;
int m_currentIndex;
int m_firstIndex;
QSortFilterProxyModel *m_filter;
bool m_valid; // selection is valid? (e.g. not hidden because it's filtered)
/**
* @brief refresh deletes all Radio Buttons and creates them new (e.g. filtering)
*/
void refresh();
/**
* @brief selectItem emits signals that tell the connected slot which item was selected
* @param row the row number of the model
* @param text the text of the radio button
* @param activate if the item was activated (see QRadioButton doc) - by default false as it not not used everytime
*/
void selectItem(int row, bool bCurrentIndexChanged, bool bActivated);
signals:
// void highlighted(int index); // not supported yet
// activated(row); // Qt::Doc: is only emitted when the change is caused by user interaction
// currentIndexChanged(row); // Qt::Doc: is always emitted regardless if the change was done programmatically or by user interaction
// My note: If the uses selects the already selected item, only activated is emitted.
/**
* @brief activated is emitted when the selection has changed or is confirmded by the user
* Qt::Doc: is only emitted when the change is caused by user interaction
* Addon: If the uses selects the already selected item, only activated is emitted.
*/
void activated(int index);
/**
* @brief currentIndexChanged is emitted when the current item changes (by user interaction or programmatically)
* Qt::Doc: is always emitted regardless if the change was done programmatically or by user interaction
* Addon: If the uses selects the already selected item, only activated is emitted.
*/
void currentIndexChanged(int index);
public slots:
/**
* @brief accept is called when the selection ends successfully (user double-clicked an item or clicked the 'Select' button) and emits signalSelectItem
*/
void accept() override;
/**
* @brief slotFilterChanged is called if the search / filter text changed and recreates all Radio Buttons after filtering
*/
void slotFilterChanged();
};
} // namespace LtsWidgets
+232
View File
@@ -0,0 +1,232 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsSelectionPopup</class>
<widget class="QDialog" name="LtsSelectionPopup">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>193</width>
<height>230</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="lbName">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Select an Item</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../Plugins/Styles/Dark/resources/rme_dark_style.qrc">:/style/show.svg</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leSearch">
<property name="placeholderText">
<string>Search ...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<widget class="QWidget" name="itemLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>177</width>
<height>102</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>800</width>
<height>800</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>100</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pbSelect">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>pbSelect</tabstop>
<tabstop>pbCancel</tabstop>
<tabstop>leSearch</tabstop>
</tabstops>
<resources>
<include location="../../Plugins/Styles/Dark/resources/rme_dark_style.qrc"/>
</resources>
<connections/>
</ui>
+180
View File
@@ -0,0 +1,180 @@
/**
* @RDE_HEADER@
* SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value
* $Id$
* $HeadURL$
* $Author$
*
* Created by Malte Diers, NLSE, 2018-05-07
*/
#include "lts_spinbox.h"
#include <cmath>
namespace LtsWidgets
{
LtsSpinBox::LtsSpinBox(QWidget *parent)
: QSpinBox(parent)
{
}
QValidator::State LtsSpinBox::validate(QString &input, int &pos) const
{
QIntValidator validator;
for (auto range : m_validRanges)
{
validator.setBottom(range.first);
validator.setTop(range.second);
QValidator::State state = validator.validate(input, pos);
if (state == QValidator::Invalid ||
state == QValidator::Intermediate)
return state;
}
// Now we're sure, it's an int. Therefore toInt() will always be okay:
int value = input.toInt();
value -= minimum();
return (value % singleStep() == 0) ? QValidator::Acceptable : QValidator::Intermediate;
}
void LtsSpinBox::fixup(QString &str) const
{
QSpinBox::fixup(str);
if (singleStep() == 1)
return;
bool bOk;
int value = str.toInt(&bOk);
if (!bOk)
return;
int remainder = (value - minimum()) % singleStep();
if (2 * remainder > singleStep())
value += singleStep();
value -= remainder;
if (value > maximum())
value -= singleStep();
str = QString::number(value);
}
bool LtsSpinBox::setExclusionRanges(const QVector<QPair<int,int> > &exclusionRanges, bool preserveCurrentValues)
{
bool retVal = true;
// In eigene Variable
auto newExclusions = exclusionRanges;
// Hinzufügen?
if (preserveCurrentValues)
newExclusions += m_exclusions;
// ggf. Min und Max tauschen:
for (auto &elem : newExclusions)
{
if (elem.first > elem.second)
{
int tmp = elem.first;
elem.first= elem.second;
elem.second = tmp;
}
}
// Sortieren und
std::sort(newExclusions.begin(), newExclusions.end(), [](const QPair<int,int> &a, const QPair<int,int> &b) { return a.first < b.first; });
for (int i=0; i<newExclusions.size()-1; ++i)
{
// schauen, dass sie nicht überlappen.
if (newExclusions[i].second >= newExclusions[i+1].first)
{
retVal = false;
break;
}
}
// Falls doch - oder falls out of range
if (!retVal || newExclusions[0].first <= minimum() || newExclusions[newExclusions.size()-1].second >= maximum())
{
// Falls nicht "Hinzufügen", Liste leeren. Sonst bleibt alles beim Alten.
if (!preserveCurrentValues)
m_exclusions.clear();
return false;
}
// Alles in Butter:
m_exclusions = exclusionRanges;
// Put that into validRanges...
m_validRanges.clear();
if (m_exclusions[0].first - 1 > minimum())
m_validRanges.append({minimum(), m_exclusions[0].first - 1});
for (int i=0; i<m_exclusions.size()-1; ++i)
{
// Beware if Ranges join each other: {1..2} {3..7}
if (m_exclusions[i].second + 1 < m_exclusions[i+1].first - 1)
m_validRanges.append({m_exclusions[i].second + 1, m_exclusions[i+1].first - 1});
}
if (m_exclusions[m_exclusions.size()-1].second + 1 < maximum())
m_validRanges.append({m_exclusions[0].first - 1, maximum()});
return true;
}
bool LtsSpinBox::addExclusionRange(int min, int max)
{
return setExclusionRanges({{min, max}}, true);
}
void LtsSpinBox::clearExclusionRanges()
{
m_exclusions.clear();
}
LtsDoubleSpinBox::LtsDoubleSpinBox(QWidget *parent)
: QDoubleSpinBox(parent)
{
}
QValidator::State LtsDoubleSpinBox::validate(QString &input, int &pos) const
{
QDoubleValidator validator;
validator.setBottom(minimum());
validator.setTop(maximum());
QValidator::State state = validator.validate(input, pos);
if (state == QValidator::Invalid ||
state == QValidator::Intermediate)
return state;
// Now we're sure, it's an int. Therefore toInt() will always be okay:
double value = input.toDouble();
value -= minimum();
return qFuzzyCompare(std::fmod(value, singleStep()), 0.0) ? QValidator::Acceptable : QValidator::Intermediate;
}
void LtsDoubleSpinBox::fixup(QString &str) const
{
QDoubleSpinBox::fixup(str);
if (qFuzzyCompare(singleStep(), pow(10, decimals())))
return;
bool bOk;
double value = str.toDouble(&bOk);
if (!bOk)
return;
double remainder = std::fmod(value - minimum(), singleStep());
if (2 * remainder > singleStep())
value += singleStep();
value -= remainder;
if (value > maximum())
value -= singleStep();
str = QString::number(value, 'f', decimals());
}
} // namespace LtsWidgets
+67
View File
@@ -0,0 +1,67 @@
/**
* @RDE_HEADER@
* SpinBox, that corrects values to a valid value that fits minimum() + n * singleStep() = value
* $Id$
* $HeadURL$
* $Author$
*
* Created by Malte Diers, NLSE, 2018-05-07
*/
#pragma once
#include "lts_widgets_export.h"
#include <QSpinBox>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsSpinBox : public QSpinBox
{
Q_OBJECT
public:
explicit LtsSpinBox(QWidget *parent = nullptr);
~LtsSpinBox() = default;
public slots:
void setValue(int val) { QString valStr = QString::number(val); fixup(valStr); QSpinBox::setValue(valStr.toInt()); }
bool setExclusionRanges(const QVector<QPair<int,int> > &exclusionRanges, bool preserveCurrentValues = false);
bool addExclusionRange(int min, int max);
void clearExclusionRanges();
protected:
QValidator::State validate(QString &input, int &pos) const override;
void fixup(QString &str) const override;
private:
struct
{
bool operator()(const QPair<int,int> &a, const QPair<int,int> &b) const { return a.first < b.first; }
} customless;
QVector<QPair<int,int> > m_exclusions;
QVector<QPair<int,int> > m_validRanges; // Doubles the information. However, it saves calculation...
};
class LTSWIDGETS_EXPORT LtsDoubleSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit LtsDoubleSpinBox(QWidget *parent = nullptr);
~LtsDoubleSpinBox() = default;
public slots:
void setValue(double val) { QString valStr = QString::number(val); fixup(valStr); QDoubleSpinBox::setValue(valStr.toDouble()); }
protected:
QValidator::State validate(QString &input, int &pos) const override;
void fixup(QString &str) const override;
};
}
+81
View File
@@ -0,0 +1,81 @@
#include "lts_splash.h"
#include <LtsWidgets/lts_global_wait_dialog.h>
#include <QCoreApplication>
#include <QFile>
#include <QEvent>
namespace LtsWidgets
{
Splash::Splash()
: QSplashScreen()
, m_done(false)
{
static const auto splashFiles =
{
QStringLiteral(":/project/splash.png"),
QStringLiteral(":/project/splash.jpg"),
};
for (const QString &f : splashFiles)
{
if (!QFile::exists(f))
continue;
setPixmap(QPixmap(f));
break;
}
}
bool Splash::event(QEvent *e)
{
if (e->type() == QEvent::MouseButtonPress ||
e->type() == QEvent::MouseButtonRelease ||
e->type() == QEvent::MouseButtonDblClick)
{
e->ignore();
return false;
}
return QSplashScreen::event(e);
}
void Splash::setMessage(const QString &message)
{
showMessage(message, Qt::AlignRight | Qt::AlignBottom);
}
void Splash::done()
{
if (m_done)
return;
m_done = true;
hide();
LtsWidgets::LtsGlobalWaitDialog::getInstance()->setVisible(true); // after the splash has finished, wait dlgs are allowed
}
static Splash *s_splashInstance = nullptr;
void Splash::cleanupSplashInstance()
{
delete s_splashInstance;
s_splashInstance = nullptr;
}
Splash *Splash::getInstance()
{
if (!s_splashInstance)
{
s_splashInstance = new Splash;
qAddPostRoutine(&Splash::cleanupSplashInstance);
}
return s_splashInstance;
}
} // namespace LtsWidgets
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <LtsWidgets/lts_widgets_export.h>
#include <QSplashScreen>
#include <QObject>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT Splash : public QSplashScreen
{
Q_OBJECT
public:
static Splash* getInstance();
void done();
void setMessage(const QString &message);
private:
Splash();
static void cleanupSplashInstance();
bool event(QEvent *e) override;
bool m_done;
};
} // namespace LtsWidgets
+62
View File
@@ -0,0 +1,62 @@
/**
* @RDE_HEADER@
* Splitter with sizes Proberty for Animation
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_splitter.h"
#include "lts_splitter_handle.h"
namespace LtsWidgets
{
LtsSplitter::LtsSplitter(QWidget *parent)
: QSplitter(parent)
{
connect(this, &LtsSplitter::sizesChanged, this, &LtsSplitter::changeSizes);
}
LtsSplitter::~LtsSplitter()
{
}
QSplitterHandle *LtsSplitter::createHandle()
{
LtsSplitterHandle *hand = new LtsSplitterHandle(orientation(), this);
#ifdef TABLET_MODE
setHandleWidth(25);
#else
setHandleWidth(10);
#endif
return hand;
}
void LtsSplitter::collapseChild(const int index)
{
if (children().size() > index)
{
QWidget *widget = static_cast<QWidget*>(children().at(index));
if (widget)
{
QList<int> s = sizes();
s[index] = 0;
setSizes(s);
}
}
}
void LtsSplitter::animate(int handleId)
{
LtsSplitterHandle *handle = dynamic_cast<LtsSplitterHandle*>(this->handle(handleId));
handle->animate();
}
void LtsSplitter::changeSizes(const QList<int> &list)
{
QSplitter::setSizes(list);
}
} // namespace LtsWidgets
+42
View File
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* Splitter with sizes Proberty for Animation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "lts_base.h"
#include "lts_widgets_export.h"
#include <QSplitter>
#include <QList>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsSplitter : public QSplitter
{
Q_OBJECT
Q_PROPERTY(QList<int> sizes READ getSizes WRITE setSizes NOTIFY sizesChanged)
public:
explicit LtsSplitter(QWidget *parent = nullptr);
~LtsSplitter();
QSplitterHandle *createHandle();
void collapseChild(const int index);
QList<int> getSizes() const { return sizes(); }
void animate(int handleId);
signals:
void sizesChanged(const QList<int> &newSizes);
public slots:
void changeSizes(const QList<int> &list);
};
} // namespace LtsWidgets
+92
View File
@@ -0,0 +1,92 @@
/**
* @RDE_HEADER@
* Splitter with sizes Proberty for Animation
* $Id$
* $HeadURL$
* $Author kemayou_s$
*/
#include "lts_splitter_handle.h"
#include <QPainter>
#include <QPaintEvent>
#include <QBrush>
#include <QEvent>
#include <QLinearGradient>
#include <QDebug>
namespace LtsWidgets
{
LtsSplitterHandle::LtsSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
: QSplitterHandle(orientation, parent)
{
auto bgrole = backgroundRole();
QPalette p = palette();
m_endColor = p.color(bgrole);
m_colorAnimation = new QPropertyAnimation(this, "splittercolor");
m_colorAnimation->setDuration(1000);
m_colorAnimation->setStartValue(m_currentColor);
m_colorAnimation->setEndValue(m_endColor);
m_colorAnimation->setLoopCount(2);
connect(m_colorAnimation, &QPropertyAnimation::finished, this, [this]{
m_animate = false;
m_currentColor = m_endColor;
repaint();
});
}
LtsSplitterHandle::~LtsSplitterHandle()
{
}
void LtsSplitterHandle::paintEvent(QPaintEvent *event)
{
if(!m_animate)
{
QSplitterHandle::paintEvent(event);
return;
}
QPainter painter(this);
QLinearGradient gradient;
gradient.setColorAt(0, m_currentColor);
//gradient.setColorAt(1, m_endColor);
if(orientation() == Qt::Horizontal)
{
gradient.setStart(rect().left(), rect().height()/2);
gradient.setFinalStop(rect().height(), rect().height()/2);
}else
{
gradient.setStart(rect().width()/2, rect().top());
gradient.setFinalStop(rect().width()/2, rect().bottom()/2);
}
painter.fillRect(event->rect(), QBrush(gradient));
}
void LtsSplitterHandle::animate()
{
if (m_colorAnimation->state() == QPropertyAnimation::Running)
return;
m_currentColor = QColor(239,87,50);
m_animate = true;
m_colorAnimation->start();
}
QColor LtsSplitterHandle::splitterColor() const
{
return m_currentColor;
}
void LtsSplitterHandle::setCurrentColor(const QColor &currentcolor)
{
if (currentcolor == m_currentColor)
return;
m_currentColor = currentcolor;
repaint();
}
} // namespace LtsWidgets
+44
View File
@@ -0,0 +1,44 @@
/**
* @RDE_HEADER@
* Splitter with sizes Proberty for Animation
* $Id$
* $HeadURL$
* $Author kemayou_s$
*/
#pragma once
#include "lts_base.h"
#include "lts_widgets_export.h"
#include <QSplitterHandle>
#include <QList>
#include <QPropertyAnimation>
namespace LtsWidgets
{
class LTSWIDGETS_EXPORT LtsSplitterHandle : public QSplitterHandle
{
Q_OBJECT
Q_PROPERTY(QColor splittercolor READ splitterColor WRITE setCurrentColor DESIGNABLE false)
public:
explicit LtsSplitterHandle(Qt::Orientation orientation, QSplitter *parent = nullptr);
~LtsSplitterHandle();
void paintEvent(QPaintEvent *event) override;
void animate();
QColor splitterColor() const;
void setCurrentColor(const QColor &currentcolor);
private:
bool m_animate {false};
QColor m_endColor {Qt::black};
QColor m_currentColor{QColor(239,87,50)};
QPropertyAnimation *m_colorAnimation;
};
} // namespace LtsWidgets
+125
View File
@@ -0,0 +1,125 @@
/**
* @RDE_HEADER@
* TabBar with special stylesheet settings
* $Id$
* $HeadURL$
* $Author$
*/
#include "lts_tab_bar.h"
#include "lts_dialog.h"
#include "lts_dialog_helper.h"
#include "lts_events.h"
#include "lts_tab_widget.h"
#include <TabBarTabStyle/tab_bar_tab_style.h>
#include <QCoreApplication>
#include <QMenu>
namespace LtsWidgets
{
LtsTabBar::LtsTabBar(bool isSecondary, QWidget *parent)
: QTabBar(parent)
, m_secondary(isSecondary)
{
setDocumentMode(true);
setTabsClosable(true);
connect(this, &LtsTabBar::currentChanged, this, &LtsTabBar::updateCloseButtonColors);
if (m_secondary)
{
connect(this, &LtsTabBar::customContextMenuRequested,
this, &LtsTabBar::onCustomContextMenuRequested);
setContextMenuPolicy(Qt::CustomContextMenu);
}
}
void LtsTabBar::_internal_closeTab() // Do not use.
{
QObject *object = sender();
int tabToClose = -1;
for (int i = 0; i < count(); i++)
{
if (tabButton(i, QTabBar::RightSide) == object)
{
tabToClose = i;
break;
}
}
if (tabToClose != -1)
emit tabCloseRequested(tabToClose);
}
void LtsTabBar::updateCloseButtonColors(int newIndex) // as this does not happen automatically
{
// tell all buttons if there are selected or not
if (newIndex == -1)
newIndex = currentIndex(); // TODO: is currentIndex already set when emitting currentChanged ??
for (int i = 0; i < count(); i++)
{
QWidget *w = tabButton(i, QTabBar::RightSide);
CloseButton *cb = qobject_cast<CloseButton *>(w);
if (!cb)
continue;
QColor text;
bool enabled = isTabEnabled(i);
bool selected = (i == newIndex);
bool secondary = m_secondary;
if (enabled)
{
if (selected)
text = (secondary) ? Styles::TabBarTabStyle::secondarySelected : Styles::TabBarTabStyle::primarySelected;
else
text = (secondary) ? Styles::TabBarTabStyle::secondaryNormal : Styles::TabBarTabStyle::primaryNormal;
}
else
text = (secondary) ? Styles::TabBarTabStyle::secondaryDisabled : Styles::TabBarTabStyle::primaryDisabled; // no 'selected' on disabled!
//qWarning() << "("<<i<<")"<<text<<text.isValid();
cb->setFontColor(text);
}
}
void LtsTabBar::onCustomContextMenuRequested(const QPoint pos)
{
if (!m_secondary)
return;
QMenu m;
QAction *a = new QAction(tr("Save as image"));
connect(a, &QAction::triggered, this, [this, pos]() { LtsTabBar::onSaveCurrentWidgetAsImage(pos); });
m.addAction(a);
m.exec(mapToGlobal(pos));
}
void LtsTabBar::onSaveCurrentWidgetAsImage(const QPoint pos)
{
int curTab = tabAt(pos);
if (curTab < 0)
curTab = currentIndex();
if (curTab < 0)
return;
LtsTabWidget *tw = qobject_cast<LtsTabWidget*>(parentWidget());
QWidget *w = tw ? tw->widget(curTab) : nullptr;
if (!w)
return;
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper*>(w);
if (helper)
w = helper->getDialog();
SaveImageEvent *ev = new SaveImageEvent(w, w->grab());
ev->setAccepted(false);
QCoreApplication::postEvent(qApp, ev);
}
} // namespace LtsWidgets
Loaded 100 of 16245 files, more files were not shown because too many files have changed in this diff. Show more