Initial commit
22
.clang-format
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
AccessModifierOffset: -8
|
||||||
|
AlignAfterOpenBracket: true
|
||||||
|
AlignEscapedNewlinesLeft: false
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
BasedOnStyle: Google
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBraces: Linux
|
||||||
|
BreakConstructorInitializersBeforeComma: true
|
||||||
|
ColumnLimit: 0
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentWidth: 8
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
PointerAlignment: Right
|
||||||
|
UseTab: Always
|
46
.gitignore
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
build
|
||||||
|
tags
|
||||||
|
|
||||||
|
# C++ objects and libs
|
||||||
|
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lai
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Qt-es
|
||||||
|
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
*.pro.user
|
||||||
|
*.pro.user.*
|
||||||
|
*.qbs.user
|
||||||
|
*.qbs.user.*
|
||||||
|
*.moc
|
||||||
|
CMakeLists.txt.user
|
||||||
|
moc_*.cpp
|
||||||
|
qrc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
Makefile*
|
||||||
|
*-build-*
|
||||||
|
|
||||||
|
# QtCreator
|
||||||
|
|
||||||
|
*.autosave
|
||||||
|
|
||||||
|
#QtCtreator Qml
|
||||||
|
*.qmlproject.user
|
||||||
|
*.qmlproject.user.*
|
||||||
|
|
||||||
|
#####=== CMake ===#####
|
||||||
|
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
|
104
CMakeLists.txt
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
|
||||||
|
project(nheko CXX)
|
||||||
|
|
||||||
|
set(nheko_VERSION_MAJOR 0)
|
||||||
|
set(nheko_VERSION_MINOR 1)
|
||||||
|
set(nheko_VERSION_PATCH 0)
|
||||||
|
|
||||||
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
find_package(Qt5Network REQUIRED)
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER gcc)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS
|
||||||
|
"${CMAKE_CXX_FLAGS} \
|
||||||
|
-std=gnu++11 \
|
||||||
|
-Wall \
|
||||||
|
-Wextra \
|
||||||
|
-Werror \
|
||||||
|
-pedantic")
|
||||||
|
|
||||||
|
set(SRC_FILES
|
||||||
|
src/ChatPage.cc
|
||||||
|
src/Deserializable.cc
|
||||||
|
src/HistoryView.cc
|
||||||
|
src/HistoryViewItem.cc
|
||||||
|
src/HistoryViewManager.cc
|
||||||
|
src/InputValidator.cc
|
||||||
|
src/Login.cc
|
||||||
|
src/LoginPage.cc
|
||||||
|
src/MainWindow.cc
|
||||||
|
src/MatrixClient.cc
|
||||||
|
src/Profile.cc
|
||||||
|
src/RoomInfo.cc
|
||||||
|
src/RoomInfoListItem.cc
|
||||||
|
src/RoomList.cc
|
||||||
|
src/RegisterPage.cc
|
||||||
|
src/SlidingStackWidget.cc
|
||||||
|
src/Sync.cc
|
||||||
|
src/TextInputWidget.cc
|
||||||
|
src/TopRoomBar.cc
|
||||||
|
src/UserInfoWidget.cc
|
||||||
|
src/WelcomePage.cc
|
||||||
|
src/main.cc
|
||||||
|
|
||||||
|
src/ui/Avatar.cc
|
||||||
|
src/ui/Badge.cc
|
||||||
|
src/ui/FlatButton.cc
|
||||||
|
src/ui/RaisedButton.cc
|
||||||
|
src/ui/Ripple.cc
|
||||||
|
src/ui/RippleOverlay.cc
|
||||||
|
src/ui/OverlayWidget.cc
|
||||||
|
src/ui/TextField.cc
|
||||||
|
src/ui/Theme.cc
|
||||||
|
src/ui/ThemeManager.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(include)
|
||||||
|
include_directories(include/ui)
|
||||||
|
|
||||||
|
qt5_wrap_ui (UI_HEADERS
|
||||||
|
forms/ChatPage.ui
|
||||||
|
forms/MainWindow.ui
|
||||||
|
forms/RoomList.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
qt5_wrap_cpp(MOC_HEADERS
|
||||||
|
include/ChatPage.h
|
||||||
|
include/HistoryView.h
|
||||||
|
include/HistoryViewItem.h
|
||||||
|
include/HistoryViewManager.h
|
||||||
|
include/LoginPage.h
|
||||||
|
include/MainWindow.h
|
||||||
|
include/MatrixClient.h
|
||||||
|
include/RegisterPage.h
|
||||||
|
include/RoomInfoListItem.h
|
||||||
|
include/RoomList.h
|
||||||
|
include/UserInfoWidget.h
|
||||||
|
include/SlidingStackWidget.h
|
||||||
|
include/TopRoomBar.h
|
||||||
|
include/TextInputWidget.h
|
||||||
|
include/WelcomePage.h
|
||||||
|
|
||||||
|
include/ui/Avatar.h
|
||||||
|
include/ui/Badge.h
|
||||||
|
include/ui/FlatButton.h
|
||||||
|
include/ui/OverlayWidget.h
|
||||||
|
include/ui/RaisedButton.h
|
||||||
|
include/ui/Ripple.h
|
||||||
|
include/ui/RippleOverlay.h
|
||||||
|
include/ui/TextField.h
|
||||||
|
include/ui/Theme.h
|
||||||
|
include/ui/ThemeManager.h
|
||||||
|
)
|
||||||
|
|
||||||
|
qt5_add_resources(QRC resources/res.qrc)
|
||||||
|
|
||||||
|
add_executable (nheko ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC})
|
||||||
|
target_link_libraries (nheko Qt5::Widgets Qt5::Network)
|
674
LICENSE
Normal file
|
@ -0,0 +1,674 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
{{ project }} Copyright (C) {{ year }} {{ organization }}
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
153
forms/ChatPage.ui
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ChatPage</class>
|
||||||
|
<widget class="QWidget" name="ChatPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>798</width>
|
||||||
|
<height>519</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color: #171919;
|
||||||
|
</string>
|
||||||
|
</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>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="sideBarLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="sideBarWidget" native="true">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||||
|
<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>
|
||||||
|
<layout class="QVBoxLayout" name="sideBarTopLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="sideBarTopWidget" native="true">
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color: #1c3133;
|
||||||
|
color: #ebebeb;</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="sideBarTopUserInfoLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="sideBarMainLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="contentLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="topBarLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="mainContentLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../resources/res.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
42
forms/MainWindow.ui
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>850</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>850</width>
|
||||||
|
<height>600</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Open Sans</family>
|
||||||
|
<stylestrategy>PreferAntialias</stylestrategy>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string notr="true">nheko - Matrix Desktop Client </string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color: #f9f9f9</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget"/>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
157
forms/RoomList.ui
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>RoomList</class>
|
||||||
|
<widget class="QWidget" name="RoomList">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>423</width>
|
||||||
|
<height>500</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>500</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QWidget {
|
||||||
|
background-color: #171919;
|
||||||
|
color: #ebebeb;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar:vertical {
|
||||||
|
background-color: #171919;
|
||||||
|
width: 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 0px 2px 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::handle:vertical {
|
||||||
|
border-radius: 50px;
|
||||||
|
background-color: #1c3133;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::add-line:vertical {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::sub-line:vertical {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<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>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</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::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>419</width>
|
||||||
|
<height>496</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<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>
|
||||||
|
<layout class="QVBoxLayout" name="scrollVerticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
88
include/ChatPage.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATPAGE_H
|
||||||
|
#define CHATPAGE_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "HistoryViewManager.h"
|
||||||
|
#include "MatrixClient.h"
|
||||||
|
#include "RoomInfo.h"
|
||||||
|
#include "RoomList.h"
|
||||||
|
#include "TextInputWidget.h"
|
||||||
|
#include "TopRoomBar.h"
|
||||||
|
#include "UserInfoWidget.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class ChatPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChatPage(QWidget *parent = 0);
|
||||||
|
~ChatPage();
|
||||||
|
|
||||||
|
// Initialize all the components of the UI.
|
||||||
|
void bootstrap(QString userid, QString homeserver, QString token);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Updates the user info box.
|
||||||
|
void updateOwnProfileInfo(QUrl avatar_url, QString display_name);
|
||||||
|
void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
|
||||||
|
void initialSyncCompleted(SyncResponse response);
|
||||||
|
void syncCompleted(SyncResponse response);
|
||||||
|
void changeTopRoomInfo(const RoomInfo &info);
|
||||||
|
void sendTextMessage(const QString &msg);
|
||||||
|
void messageSent(const QString event_id, int txn_id);
|
||||||
|
void startSync();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::ChatPage *ui;
|
||||||
|
|
||||||
|
void setOwnAvatar(QByteArray img);
|
||||||
|
|
||||||
|
RoomList *room_list_;
|
||||||
|
HistoryViewManager *view_manager_;
|
||||||
|
|
||||||
|
TopRoomBar *top_bar_;
|
||||||
|
TextInputWidget *text_input_;
|
||||||
|
|
||||||
|
QTimer *sync_timer_;
|
||||||
|
int sync_interval_;
|
||||||
|
|
||||||
|
RoomInfo current_room_;
|
||||||
|
QMap<QString, QPixmap> room_avatars_;
|
||||||
|
|
||||||
|
UserInfoWidget *user_info_widget_;
|
||||||
|
|
||||||
|
// Matrix client
|
||||||
|
MatrixClient *matrix_client_;
|
||||||
|
|
||||||
|
// Used for one off media requests.
|
||||||
|
QNetworkAccessManager *content_downloader_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATPAGE_H
|
52
include/Deserializable.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DESERIALIZABLE_H
|
||||||
|
#define DESERIALIZABLE_H
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
class DeserializationException : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DeserializationException(const std::string &msg);
|
||||||
|
virtual const char *what() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// JSON response structs need to implement the interface.
|
||||||
|
class Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void deserialize(QJsonValue) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual void deserialize(QJsonObject) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual void deserialize(QJsonDocument) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
62
include/HistoryView.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HISTORY_VIEW_H
|
||||||
|
#define HISTORY_VIEW_H
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QList>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "HistoryViewItem.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
class HistoryView : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HistoryView(QWidget *parent = 0);
|
||||||
|
explicit HistoryView(QList<Event> events, QWidget *parent = 0);
|
||||||
|
~HistoryView();
|
||||||
|
|
||||||
|
void addHistoryItem(Event event, QString color, bool with_sender);
|
||||||
|
void addEvents(const QList<Event> &events);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void sliderRangeChanged(int min, int max);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const QList<QString> COLORS;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
QString chooseRandomColor();
|
||||||
|
|
||||||
|
QVBoxLayout *top_layout_;
|
||||||
|
QVBoxLayout *scroll_layout_;
|
||||||
|
|
||||||
|
QScrollArea *scroll_area_;
|
||||||
|
QWidget *scroll_widget_;
|
||||||
|
|
||||||
|
QString last_sender_;
|
||||||
|
QMap<QString, QString> nick_colors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HISTORY_VIEW_H
|
41
include/HistoryViewItem.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HISTORY_VIEW_ITEM_H
|
||||||
|
#define HISTORY_VIEW_ITEM_H
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
class HistoryViewItem : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
HistoryViewItem(Event event, bool with_sender, QString color, QWidget *parent = 0);
|
||||||
|
~HistoryViewItem();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHBoxLayout *top_layout_;
|
||||||
|
|
||||||
|
QLabel *time_label_;
|
||||||
|
QLabel *content_label_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HISTORY_VIEW_ITEM_H
|
47
include/HistoryViewManager.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HISTORY_VIEW_MANAGER_H
|
||||||
|
#define HISTORY_VIEW_MANAGER_H
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "HistoryView.h"
|
||||||
|
#include "RoomInfo.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
class HistoryViewManager : public QStackedWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
HistoryViewManager(QWidget *parent);
|
||||||
|
~HistoryViewManager();
|
||||||
|
|
||||||
|
void initialize(const Rooms &rooms);
|
||||||
|
void sync(const Rooms &rooms);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setHistoryView(const RoomInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, HistoryView *> views_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
49
include/InputValidator.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MATRIXIDVALIDATOR_H
|
||||||
|
#define MATRIXIDVALIDATOR_H
|
||||||
|
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QRegExpValidator>
|
||||||
|
|
||||||
|
class InputValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputValidator(QObject *parent = 0);
|
||||||
|
|
||||||
|
// Validators for the different types of input.
|
||||||
|
QRegExpValidator *id_;
|
||||||
|
QRegExpValidator *localpart_;
|
||||||
|
QRegExpValidator *password_;
|
||||||
|
QRegExpValidator *domain_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Regular expression used to validate the whole matrix id.
|
||||||
|
const QRegExp matrix_id_;
|
||||||
|
|
||||||
|
// Regular expressino to validate the matrix localpart.
|
||||||
|
const QRegExp matrix_localpart_;
|
||||||
|
|
||||||
|
// Regular expression to validate a password for a matrix account.
|
||||||
|
const QRegExp matrix_password_;
|
||||||
|
|
||||||
|
// Regular expression to validate a domain name.
|
||||||
|
const QRegExp server_domain_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MATRIXIDVALIDATOR_H
|
56
include/Login.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOGIN_H
|
||||||
|
#define LOGIN_H
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
|
||||||
|
class LoginRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LoginRequest();
|
||||||
|
LoginRequest(QString username, QString password);
|
||||||
|
|
||||||
|
QByteArray serialize();
|
||||||
|
|
||||||
|
void setPassword(QString password);
|
||||||
|
void setUser(QString username);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString user_;
|
||||||
|
QString password_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LoginResponse : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void deserialize(QJsonDocument data) throw(DeserializationException) override;
|
||||||
|
|
||||||
|
QString getAccessToken();
|
||||||
|
QString getHomeServer();
|
||||||
|
QString getUserId();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString access_token_;
|
||||||
|
QString home_server_;
|
||||||
|
QString user_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOGIN_H
|
80
include/LoginPage.h
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOGINPAGE_H
|
||||||
|
#define LOGINPAGE_H
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "FlatButton.h"
|
||||||
|
#include "InputValidator.h"
|
||||||
|
#include "RaisedButton.h"
|
||||||
|
#include "TextField.h"
|
||||||
|
|
||||||
|
class LoginPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LoginPage(QWidget *parent = 0);
|
||||||
|
~LoginPage();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void backButtonClicked();
|
||||||
|
|
||||||
|
// Emitted after the matrix ID validation. The handler should be
|
||||||
|
// responsible for performing the actual login with a remote server.
|
||||||
|
void userLogin(const QString &username, const QString &password, const QString home_server);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Displays errors produced during the login.
|
||||||
|
void loginError(QString error_message);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
// Callback for the back button.
|
||||||
|
void onBackButtonClicked();
|
||||||
|
|
||||||
|
// Callback for the login button.
|
||||||
|
void onLoginButtonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout *top_layout_;
|
||||||
|
|
||||||
|
QHBoxLayout *back_layout_;
|
||||||
|
QHBoxLayout *logo_layout_;
|
||||||
|
QHBoxLayout *button_layout_;
|
||||||
|
|
||||||
|
QLabel *logo_;
|
||||||
|
QLabel *error_label_;
|
||||||
|
|
||||||
|
FlatButton *back_button_;
|
||||||
|
RaisedButton *login_button_;
|
||||||
|
|
||||||
|
QWidget *form_widget_;
|
||||||
|
QHBoxLayout *form_wrapper_;
|
||||||
|
QVBoxLayout *form_layout_;
|
||||||
|
|
||||||
|
TextField *username_input_;
|
||||||
|
TextField *password_input_;
|
||||||
|
|
||||||
|
InputValidator *matrix_id_validator_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOGINPAGE_H
|
84
include/MainWindow.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include "ChatPage.h"
|
||||||
|
#include "LoginPage.h"
|
||||||
|
#include "MatrixClient.h"
|
||||||
|
#include "RegisterPage.h"
|
||||||
|
#include "SlidingStackWidget.h"
|
||||||
|
#include "WelcomePage.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Show the welcome page in the main window.
|
||||||
|
void showWelcomePage();
|
||||||
|
|
||||||
|
// Show the login page in the main window.
|
||||||
|
void showLoginPage();
|
||||||
|
|
||||||
|
// Show the register page in the main window.
|
||||||
|
void showRegisterPage();
|
||||||
|
|
||||||
|
// Show the chat page and start communicating with the given access token.
|
||||||
|
void showChatPage(QString user_id, QString home_server, QString token);
|
||||||
|
|
||||||
|
// Performs the actual login.
|
||||||
|
void matrixLogin(const QString &username, const QString &password, const QString &home_server);
|
||||||
|
|
||||||
|
// Performs the actual registration.
|
||||||
|
void matrixRegister(const QString &username, const QString &password, const QString &server);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The UI component of the main window.
|
||||||
|
Ui::MainWindow *ui_;
|
||||||
|
|
||||||
|
// The initial welcome screen.
|
||||||
|
WelcomePage *welcome_page_;
|
||||||
|
|
||||||
|
// The login screen.
|
||||||
|
LoginPage *login_page_;
|
||||||
|
|
||||||
|
// The register page.
|
||||||
|
RegisterPage *register_page_;
|
||||||
|
|
||||||
|
// A stacked widget that handles the transitions between widgets.
|
||||||
|
SlidingStackWidget *sliding_stack_;
|
||||||
|
|
||||||
|
// The main chat area.
|
||||||
|
ChatPage *chat_page_;
|
||||||
|
|
||||||
|
MatrixClient *matrix_client_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
134
include/MatrixClient.h
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MATRIXCLIENT_H
|
||||||
|
#define MATRIXCLIENT_H
|
||||||
|
|
||||||
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
|
|
||||||
|
#include "Profile.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MatrixClient provides the high level API to communicate with
|
||||||
|
* a Matrix homeserver. All the responses are returned through signals.
|
||||||
|
*/
|
||||||
|
class MatrixClient : public QNetworkAccessManager
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MatrixClient(QString server, QObject *parent = 0);
|
||||||
|
~MatrixClient();
|
||||||
|
|
||||||
|
// Client API.
|
||||||
|
void initialSync();
|
||||||
|
void sync();
|
||||||
|
void sendTextMessage(QString roomid, QString msg);
|
||||||
|
void login(const QString &username, const QString &password);
|
||||||
|
void registerUser(const QString &username, const QString &password);
|
||||||
|
void versions();
|
||||||
|
|
||||||
|
inline QString getHomeServer();
|
||||||
|
inline void incrementTransactionId();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Profile
|
||||||
|
void getOwnProfile();
|
||||||
|
|
||||||
|
inline void setServer(QString server);
|
||||||
|
inline void setAccessToken(QString token);
|
||||||
|
inline void setNextBatchToken(const QString &next_batch);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Emitted after a error during the login.
|
||||||
|
void loginError(QString error);
|
||||||
|
|
||||||
|
// Emitted after succesfull user login. A new access token is returned by the server.
|
||||||
|
void loginSuccess(QString user_id, QString home_server, QString token);
|
||||||
|
|
||||||
|
// Returned profile data for the user's account.
|
||||||
|
void getOwnProfileResponse(QUrl avatar_url, QString display_name);
|
||||||
|
void initialSyncCompleted(SyncResponse response);
|
||||||
|
void syncCompleted(SyncResponse response);
|
||||||
|
void messageSent(QString event_id, int txn_id);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Endpoint {
|
||||||
|
GetOwnProfile,
|
||||||
|
GetProfile,
|
||||||
|
InitialSync,
|
||||||
|
Login,
|
||||||
|
Register,
|
||||||
|
SendTextMessage,
|
||||||
|
Sync,
|
||||||
|
Versions,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Response handlers.
|
||||||
|
void onLoginResponse(QNetworkReply *reply);
|
||||||
|
void onRegisterResponse(QNetworkReply *reply);
|
||||||
|
void onVersionsResponse(QNetworkReply *reply);
|
||||||
|
void onGetOwnProfileResponse(QNetworkReply *reply);
|
||||||
|
void onSendTextMessageResponse(QNetworkReply *reply);
|
||||||
|
void onInitialSyncResponse(QNetworkReply *reply);
|
||||||
|
void onSyncResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
|
// Client API prefix.
|
||||||
|
QString api_url_;
|
||||||
|
|
||||||
|
// The Matrix server used for communication.
|
||||||
|
QString server_;
|
||||||
|
|
||||||
|
// The access token used for authentication.
|
||||||
|
QString token_;
|
||||||
|
|
||||||
|
// Increasing transaction ID.
|
||||||
|
int txn_id_;
|
||||||
|
|
||||||
|
// Token to be used for the next sync.
|
||||||
|
QString next_batch_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline QString MatrixClient::getHomeServer()
|
||||||
|
{
|
||||||
|
return server_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MatrixClient::setServer(QString server)
|
||||||
|
{
|
||||||
|
server_ = "https://" + server;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MatrixClient::setAccessToken(QString token)
|
||||||
|
{
|
||||||
|
token_ = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MatrixClient::setNextBatchToken(const QString &next_batch)
|
||||||
|
{
|
||||||
|
next_batch_ = next_batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MatrixClient::incrementTransactionId()
|
||||||
|
{
|
||||||
|
txn_id_ += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MATRIXCLIENT_H
|
39
include/Profile.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROFILE_H
|
||||||
|
#define PROFILE_H
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
|
||||||
|
class ProfileResponse : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void deserialize(QJsonDocument data) throw(DeserializationException) override;
|
||||||
|
|
||||||
|
QUrl getAvatarUrl();
|
||||||
|
QString getDisplayName();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUrl avatar_url_;
|
||||||
|
QString display_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PROFILE_H
|
74
include/RegisterPage.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REGISTERPAGE_H
|
||||||
|
#define REGISTERPAGE_H
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "FlatButton.h"
|
||||||
|
#include "InputValidator.h"
|
||||||
|
#include "RaisedButton.h"
|
||||||
|
#include "TextField.h"
|
||||||
|
|
||||||
|
class RegisterPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RegisterPage(QWidget *parent = 0);
|
||||||
|
~RegisterPage();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void backButtonClicked();
|
||||||
|
|
||||||
|
// Emitted after successful input validation. The handler should be
|
||||||
|
// responsible for the actual registering on the remote Matrix server.
|
||||||
|
void registerUser(const QString &username, const QString &password, const QString &server);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onBackButtonClicked();
|
||||||
|
void onRegisterButtonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout *top_layout_;
|
||||||
|
|
||||||
|
QHBoxLayout *back_layout_;
|
||||||
|
QHBoxLayout *logo_layout_;
|
||||||
|
QHBoxLayout *button_layout_;
|
||||||
|
|
||||||
|
QLabel *logo_;
|
||||||
|
|
||||||
|
FlatButton *back_button_;
|
||||||
|
RaisedButton *register_button_;
|
||||||
|
|
||||||
|
QWidget *form_widget_;
|
||||||
|
QHBoxLayout *form_wrapper_;
|
||||||
|
QVBoxLayout *form_layout_;
|
||||||
|
|
||||||
|
TextField *username_input_;
|
||||||
|
TextField *password_input_;
|
||||||
|
TextField *password_confirmation_;
|
||||||
|
TextField *server_input_;
|
||||||
|
|
||||||
|
InputValidator *validator_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // REGISTERPAGE_H
|
49
include/RoomInfo.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ROOM_INFO_H
|
||||||
|
#define ROOM_INFO_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class RoomInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RoomInfo();
|
||||||
|
RoomInfo(QString name, QString topic = "", QUrl avatar_url = QUrl(""));
|
||||||
|
|
||||||
|
QString id() const;
|
||||||
|
QString name() const;
|
||||||
|
QString topic() const;
|
||||||
|
QUrl avatarUrl() const;
|
||||||
|
|
||||||
|
void setAvatarUrl(const QUrl &url);
|
||||||
|
void setId(const QString &id);
|
||||||
|
void setName(const QString &name);
|
||||||
|
void setTopic(const QString &name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString id_;
|
||||||
|
QString name_;
|
||||||
|
QString topic_;
|
||||||
|
QUrl avatar_url_;
|
||||||
|
QList<QString> aliases_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ROOM_INFO_H
|
93
include/RoomInfoListItem.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ROOMINFOLISTITEM_H
|
||||||
|
#define ROOMINFOLISTITEM_H
|
||||||
|
|
||||||
|
#include <QtWidgets/QHBoxLayout>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QVBoxLayout>
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
#include "RippleOverlay.h"
|
||||||
|
#include "RoomInfo.h"
|
||||||
|
|
||||||
|
class RoomInfoListItem : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RoomInfoListItem(RoomInfo info, QWidget *parent = 0);
|
||||||
|
~RoomInfoListItem();
|
||||||
|
|
||||||
|
inline bool isPressed();
|
||||||
|
inline RoomInfo info();
|
||||||
|
inline void setAvatar(const QImage &avatar_image);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clicked(RoomInfo info_);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setPressedState(bool state);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setElidedText(QLabel *label, QString text, int width);
|
||||||
|
|
||||||
|
RippleOverlay *ripple_overlay_;
|
||||||
|
|
||||||
|
RoomInfo info_;
|
||||||
|
|
||||||
|
QHBoxLayout *topLayout_;
|
||||||
|
|
||||||
|
QVBoxLayout *avatarLayout_;
|
||||||
|
QVBoxLayout *textLayout_;
|
||||||
|
|
||||||
|
QWidget *avatarWidget_;
|
||||||
|
QWidget *textWidget_;
|
||||||
|
|
||||||
|
QLabel *roomName_;
|
||||||
|
QLabel *roomTopic_;
|
||||||
|
|
||||||
|
Avatar *roomAvatar_;
|
||||||
|
|
||||||
|
QString pressed_style_;
|
||||||
|
QString normal_style_;
|
||||||
|
|
||||||
|
bool is_pressed_;
|
||||||
|
int max_height_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool RoomInfoListItem::isPressed()
|
||||||
|
{
|
||||||
|
return is_pressed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RoomInfo RoomInfoListItem::info()
|
||||||
|
{
|
||||||
|
return info_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RoomInfoListItem::setAvatar(const QImage &avatar_image)
|
||||||
|
{
|
||||||
|
roomAvatar_->setImage(avatar_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ROOMINFOLISTITEM_H
|
60
include/RoomList.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ROOMLIST_H
|
||||||
|
#define ROOMLIST_H
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "RoomInfo.h"
|
||||||
|
#include "RoomInfoListItem.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class RoomList;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomList : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RoomList(QWidget *parent = 0);
|
||||||
|
~RoomList();
|
||||||
|
|
||||||
|
void appendRoom(QString name);
|
||||||
|
void setInitialRooms(const Rooms &rooms);
|
||||||
|
void updateRoomAvatar(const QString &roomid, const QImage &avatar_image);
|
||||||
|
RoomInfo extractRoomInfo(const State &room_state);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void roomChanged(const RoomInfo &info);
|
||||||
|
void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void highlightSelectedRoom(const RoomInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::RoomList *ui;
|
||||||
|
|
||||||
|
QMap<QString, RoomInfoListItem *> available_rooms_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ROOMLIST_H
|
94
include/SlidingStackWidget.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLIDINGSTACKWIDGET_H
|
||||||
|
#define SLIDINGSTACKWIDGET_H
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QEasingCurve>
|
||||||
|
#include <QParallelAnimationGroup>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SlidingStackWidget allows smooth side shifting of widgets,
|
||||||
|
* in addition to the hard switching from one to another offered
|
||||||
|
* by QStackedWidget.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SlidingStackWidget : public QStackedWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Defines the animation direction.
|
||||||
|
enum AnimationDirection {
|
||||||
|
LEFT_TO_RIGHT,
|
||||||
|
RIGHT_TO_LEFT,
|
||||||
|
AUTOMATIC
|
||||||
|
};
|
||||||
|
|
||||||
|
SlidingStackWidget(QWidget *parent);
|
||||||
|
~SlidingStackWidget();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Move to the next widget.
|
||||||
|
void slideInNext();
|
||||||
|
|
||||||
|
// Move to the previous widget.
|
||||||
|
void slideInPrevious();
|
||||||
|
|
||||||
|
// Move to a widget by index.
|
||||||
|
void slideInIndex(int index, enum AnimationDirection direction = AnimationDirection::AUTOMATIC);
|
||||||
|
|
||||||
|
int getWidgetIndex(QWidget *widget);
|
||||||
|
signals:
|
||||||
|
// Internal signal to alert the engine for the animation's end.
|
||||||
|
void animationFinished();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
// Internal slot to handle the end of the animation.
|
||||||
|
void onAnimationFinished();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The method that does the main work for the widget transition.
|
||||||
|
void slideInWidget(QWidget *widget, enum AnimationDirection direction = AnimationDirection::AUTOMATIC);
|
||||||
|
|
||||||
|
// Indicates whether or not the animation is active.
|
||||||
|
bool active_;
|
||||||
|
|
||||||
|
// The widget currently displayed.
|
||||||
|
QWidget *window_;
|
||||||
|
|
||||||
|
// The speed of the animation in milliseconds.
|
||||||
|
int speed_;
|
||||||
|
|
||||||
|
// The animation type.
|
||||||
|
enum QEasingCurve::Type animation_type_;
|
||||||
|
|
||||||
|
// Current widget's index.
|
||||||
|
int now_;
|
||||||
|
|
||||||
|
// Reference point.
|
||||||
|
QPoint current_position_;
|
||||||
|
|
||||||
|
// Next widget's to show index.
|
||||||
|
int next_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SLIDINGSTACKWIDGET_H
|
119
include/Sync.h
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SYNC_H
|
||||||
|
#define SYNC_H
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
|
||||||
|
class Event : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QJsonObject content() const;
|
||||||
|
QJsonObject unsigned_content() const;
|
||||||
|
|
||||||
|
QString sender() const;
|
||||||
|
QString state_key() const;
|
||||||
|
QString type() const;
|
||||||
|
QString eventId() const;
|
||||||
|
|
||||||
|
uint64_t timestamp() const;
|
||||||
|
|
||||||
|
void deserialize(QJsonValue data) throw(DeserializationException) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonObject content_;
|
||||||
|
QJsonObject unsigned_;
|
||||||
|
|
||||||
|
QString sender_;
|
||||||
|
QString state_key_;
|
||||||
|
QString type_;
|
||||||
|
QString event_id_;
|
||||||
|
|
||||||
|
uint64_t origin_server_ts_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class State : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void deserialize(QJsonValue data) throw(DeserializationException) override;
|
||||||
|
QList<Event> events() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Event> events_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Timeline : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QList<Event> events() const;
|
||||||
|
QString previousBatch() const;
|
||||||
|
bool limited() const;
|
||||||
|
|
||||||
|
void deserialize(QJsonValue data) throw(DeserializationException) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Event> events_;
|
||||||
|
QString prev_batch_;
|
||||||
|
bool limited_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Add support for ehpmeral, account_data, undread_notifications
|
||||||
|
class JoinedRoom : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
State state() const;
|
||||||
|
Timeline timeline() const;
|
||||||
|
|
||||||
|
void deserialize(QJsonValue data) throw(DeserializationException) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
State state_;
|
||||||
|
Timeline timeline_;
|
||||||
|
/* Ephemeral ephemeral_; */
|
||||||
|
/* AccountData account_data_; */
|
||||||
|
/* UnreadNotifications unread_notifications_; */
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Add support for invited and left rooms.
|
||||||
|
class Rooms : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QMap<QString, JoinedRoom> join() const;
|
||||||
|
void deserialize(QJsonValue data) throw(DeserializationException) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, JoinedRoom> join_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SyncResponse : public Deserializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void deserialize(QJsonDocument data) throw(DeserializationException) override;
|
||||||
|
QString nextBatch() const;
|
||||||
|
Rooms rooms() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString next_batch_;
|
||||||
|
Rooms rooms_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SYNC_H
|
53
include/TextInputWidget.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TEXT_INPUT_WIDGET_H
|
||||||
|
#define TEXT_INPUT_WIDGET_H
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "FlatButton.h"
|
||||||
|
|
||||||
|
class TextInputWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextInputWidget(QWidget *parent = 0);
|
||||||
|
~TextInputWidget();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onSendButtonClicked();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendTextMessage(QString msg);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHBoxLayout *top_layout_;
|
||||||
|
QLineEdit *input_;
|
||||||
|
|
||||||
|
FlatButton *send_file_button_;
|
||||||
|
FlatButton *send_message_button_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXT_INPUT_WIDGET_H
|
79
include/TopRoomBar.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TOP_ROOM_BAR_H
|
||||||
|
#define TOP_ROOM_BAR_H
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
#include "FlatButton.h"
|
||||||
|
|
||||||
|
class TopRoomBar : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TopRoomBar(QWidget *parent = 0);
|
||||||
|
~TopRoomBar();
|
||||||
|
|
||||||
|
inline void updateRoomAvatar(const QImage &avatar_image);
|
||||||
|
inline void updateRoomAvatar(const QIcon &icon);
|
||||||
|
inline void updateRoomName(const QString &name);
|
||||||
|
inline void updateRoomTopic(const QString &topic);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHBoxLayout *top_layout_;
|
||||||
|
QVBoxLayout *text_layout_;
|
||||||
|
|
||||||
|
QLabel *name_label_;
|
||||||
|
QLabel *topic_label_;
|
||||||
|
|
||||||
|
FlatButton *search_button_;
|
||||||
|
FlatButton *settings_button_;
|
||||||
|
|
||||||
|
Avatar *avatar_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void TopRoomBar::updateRoomAvatar(const QImage &avatar_image)
|
||||||
|
{
|
||||||
|
avatar_->setImage(avatar_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TopRoomBar::updateRoomAvatar(const QIcon &icon)
|
||||||
|
{
|
||||||
|
avatar_->setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TopRoomBar::updateRoomName(const QString &name)
|
||||||
|
{
|
||||||
|
name_label_->setText(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TopRoomBar::updateRoomTopic(const QString &topic)
|
||||||
|
{
|
||||||
|
topic_label_->setText(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TOP_ROOM_BAR_H
|
60
include/UserInfoWidget.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef USER_INFO_WIDGET_H
|
||||||
|
#define USER_INFO_WIDGET_H
|
||||||
|
|
||||||
|
#include <QtWidgets/QHBoxLayout>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QVBoxLayout>
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
#include "FlatButton.h"
|
||||||
|
|
||||||
|
class UserInfoWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
UserInfoWidget(QWidget *parent = 0);
|
||||||
|
~UserInfoWidget();
|
||||||
|
|
||||||
|
void setAvatar(const QImage &img);
|
||||||
|
void setDisplayName(const QString &name);
|
||||||
|
void setUserId(const QString &userid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Avatar *userAvatar_;
|
||||||
|
|
||||||
|
QHBoxLayout *topLayout_;
|
||||||
|
QHBoxLayout *avatarLayout_;
|
||||||
|
QVBoxLayout *textLayout_;
|
||||||
|
QHBoxLayout *buttonLayout_;
|
||||||
|
|
||||||
|
FlatButton *settingsButton_;
|
||||||
|
|
||||||
|
QLabel *displayNameLabel_;
|
||||||
|
QLabel *userIdLabel_;
|
||||||
|
|
||||||
|
QString display_name_;
|
||||||
|
QString userid_;
|
||||||
|
|
||||||
|
QImage avatar_image_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
61
include/WelcomePage.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WELCOMEPAGE_H
|
||||||
|
#define WELCOMEPAGE_H
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "RaisedButton.h"
|
||||||
|
|
||||||
|
class WelcomePage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WelcomePage(QWidget *parent = 0);
|
||||||
|
~WelcomePage();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Notify that the user wants to login in.
|
||||||
|
void userLogin();
|
||||||
|
|
||||||
|
// Notify that the user wants to register.
|
||||||
|
void userRegister();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onLoginButtonClicked();
|
||||||
|
void onRegisterButtonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout *top_layout_;
|
||||||
|
QHBoxLayout *button_layout_;
|
||||||
|
|
||||||
|
QLabel *intro_banner_;
|
||||||
|
QLabel *intro_text_;
|
||||||
|
|
||||||
|
QSpacerItem *button_spacer_;
|
||||||
|
|
||||||
|
RaisedButton *register_button_;
|
||||||
|
RaisedButton *login_button_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WELCOMEPAGE_H
|
51
include/ui/Avatar.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef UI_AVATAR_H
|
||||||
|
#define UI_AVATAR_H
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "Theme.h"
|
||||||
|
|
||||||
|
class Avatar : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
|
||||||
|
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Avatar(QWidget *parent = 0);
|
||||||
|
~Avatar();
|
||||||
|
|
||||||
|
void setBackgroundColor(const QColor &color);
|
||||||
|
void setIcon(const QIcon &icon);
|
||||||
|
void setImage(const QImage &image);
|
||||||
|
void setLetter(const QChar &letter);
|
||||||
|
void setSize(int size);
|
||||||
|
void setTextColor(const QColor &color);
|
||||||
|
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
QColor textColor() const;
|
||||||
|
int size() const;
|
||||||
|
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
ui::AvatarType type_;
|
||||||
|
QChar letter_;
|
||||||
|
QColor background_color_;
|
||||||
|
QColor text_color_;
|
||||||
|
QIcon icon_;
|
||||||
|
QImage image_;
|
||||||
|
QPixmap pixmap_;
|
||||||
|
int size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UI_AVATAR_H
|
63
include/ui/Badge.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef UI_BADGE_H
|
||||||
|
#define UI_BADGE_H
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include "OverlayWidget.h"
|
||||||
|
|
||||||
|
class Badge : public OverlayWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
|
||||||
|
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
|
||||||
|
Q_PROPERTY(QPointF relativePosition WRITE setRelativePosition READ relativePosition)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Badge(QWidget *parent = 0);
|
||||||
|
explicit Badge(const QIcon &icon, QWidget *parent = 0);
|
||||||
|
explicit Badge(const QString &text, QWidget *parent = 0);
|
||||||
|
~Badge();
|
||||||
|
|
||||||
|
void setBackgroundColor(const QColor &color);
|
||||||
|
void setTextColor(const QColor &color);
|
||||||
|
void setIcon(const QIcon &icon);
|
||||||
|
void setRelativePosition(const QPointF &pos);
|
||||||
|
void setRelativePosition(qreal x, qreal y);
|
||||||
|
void setRelativeXPosition(qreal x);
|
||||||
|
void setRelativeYPosition(qreal y);
|
||||||
|
void setText(const QString &text);
|
||||||
|
|
||||||
|
QIcon icon() const;
|
||||||
|
QString text() const;
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
QColor textColor() const;
|
||||||
|
QPointF relativePosition() const;
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
qreal relativeXPosition() const;
|
||||||
|
qreal relativeYPosition() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
int getDiameter() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
QColor background_color_;
|
||||||
|
QColor text_color_;
|
||||||
|
|
||||||
|
QIcon icon_;
|
||||||
|
QSize size_;
|
||||||
|
QString text_;
|
||||||
|
|
||||||
|
int padding_;
|
||||||
|
|
||||||
|
qreal x_;
|
||||||
|
qreal y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UI_BADGE_H
|
210
include/ui/FlatButton.h
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
#ifndef UI_FLAT_BUTTON_H
|
||||||
|
#define UI_FLAT_BUTTON_H
|
||||||
|
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSequentialAnimationGroup>
|
||||||
|
#include <QStateMachine>
|
||||||
|
|
||||||
|
#include "RippleOverlay.h"
|
||||||
|
#include "Theme.h"
|
||||||
|
|
||||||
|
class FlatButton;
|
||||||
|
|
||||||
|
class FlatButtonStateMachine : public QStateMachine
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(qreal overlayOpacity WRITE setOverlayOpacity READ overlayOpacity)
|
||||||
|
Q_PROPERTY(qreal checkedOverlayProgress WRITE setCheckedOverlayProgress READ checkedOverlayProgress)
|
||||||
|
Q_PROPERTY(qreal haloOpacity WRITE setHaloOpacity READ haloOpacity)
|
||||||
|
Q_PROPERTY(qreal haloSize WRITE setHaloSize READ haloSize)
|
||||||
|
Q_PROPERTY(qreal haloScaleFactor WRITE setHaloScaleFactor READ haloScaleFactor)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FlatButtonStateMachine(FlatButton *parent);
|
||||||
|
~FlatButtonStateMachine();
|
||||||
|
|
||||||
|
void setOverlayOpacity(qreal opacity);
|
||||||
|
void setCheckedOverlayProgress(qreal opacity);
|
||||||
|
void setHaloOpacity(qreal opacity);
|
||||||
|
void setHaloSize(qreal size);
|
||||||
|
void setHaloScaleFactor(qreal factor);
|
||||||
|
|
||||||
|
inline qreal overlayOpacity() const;
|
||||||
|
inline qreal checkedOverlayProgress() const;
|
||||||
|
inline qreal haloOpacity() const;
|
||||||
|
inline qreal haloSize() const;
|
||||||
|
inline qreal haloScaleFactor() const;
|
||||||
|
|
||||||
|
void startAnimations();
|
||||||
|
void setupProperties();
|
||||||
|
void updateCheckedStatus();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonPressed();
|
||||||
|
void buttonChecked();
|
||||||
|
void buttonUnchecked();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addTransition(QObject *object, const char *signal, QState *fromState, QState *toState);
|
||||||
|
void addTransition(QObject *object, QEvent::Type eventType, QState *fromState, QState *toState);
|
||||||
|
void addTransition(QAbstractTransition *transition, QState *fromState, QState *toState);
|
||||||
|
|
||||||
|
FlatButton *const button_;
|
||||||
|
|
||||||
|
QState *const top_level_state_;
|
||||||
|
QState *const config_state_;
|
||||||
|
QState *const checkable_state_;
|
||||||
|
QState *const checked_state_;
|
||||||
|
QState *const unchecked_state_;
|
||||||
|
QState *const neutral_state_;
|
||||||
|
QState *const neutral_focused_state_;
|
||||||
|
QState *const hovered_state_;
|
||||||
|
QState *const hovered_focused_state_;
|
||||||
|
QState *const pressed_state_;
|
||||||
|
|
||||||
|
QSequentialAnimationGroup *const halo_animation_;
|
||||||
|
|
||||||
|
qreal overlay_opacity_;
|
||||||
|
qreal checked_overlay_progress_;
|
||||||
|
qreal halo_opacity_;
|
||||||
|
qreal halo_size_;
|
||||||
|
qreal halo_scale_factor_;
|
||||||
|
|
||||||
|
bool was_checked_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline qreal FlatButtonStateMachine::overlayOpacity() const
|
||||||
|
{
|
||||||
|
return overlay_opacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal FlatButtonStateMachine::checkedOverlayProgress() const
|
||||||
|
{
|
||||||
|
return checked_overlay_progress_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal FlatButtonStateMachine::haloOpacity() const
|
||||||
|
{
|
||||||
|
return halo_opacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal FlatButtonStateMachine::haloSize() const
|
||||||
|
{
|
||||||
|
return halo_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal FlatButtonStateMachine::haloScaleFactor() const
|
||||||
|
{
|
||||||
|
return halo_scale_factor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlatButton : public QPushButton
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor foregroundColor WRITE setForegroundColor READ foregroundColor)
|
||||||
|
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
|
||||||
|
Q_PROPERTY(QColor overlayColor WRITE setOverlayColor READ overlayColor)
|
||||||
|
Q_PROPERTY(QColor disabledForegroundColor WRITE setDisabledForegroundColor READ disabledForegroundColor)
|
||||||
|
Q_PROPERTY(QColor disabledBackgroundColor WRITE setDisabledBackgroundColor READ disabledBackgroundColor)
|
||||||
|
Q_PROPERTY(qreal fontSize WRITE setFontSize READ fontSize)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FlatButton(QWidget *parent = 0, ui::ButtonPreset preset = ui::FlatPreset);
|
||||||
|
explicit FlatButton(const QString &text, QWidget *parent = 0, ui::ButtonPreset preset = ui::FlatPreset);
|
||||||
|
FlatButton(const QString &text, ui::Role role, QWidget *parent = 0, ui::ButtonPreset preset = ui::FlatPreset);
|
||||||
|
~FlatButton();
|
||||||
|
|
||||||
|
void applyPreset(ui::ButtonPreset preset);
|
||||||
|
|
||||||
|
void setBackgroundColor(const QColor &color);
|
||||||
|
void setBackgroundMode(Qt::BGMode mode);
|
||||||
|
void setBaseOpacity(qreal opacity);
|
||||||
|
void setCheckable(bool value);
|
||||||
|
void setCornerRadius(qreal radius);
|
||||||
|
void setDisabledBackgroundColor(const QColor &color);
|
||||||
|
void setDisabledForegroundColor(const QColor &color);
|
||||||
|
void setFixedRippleRadius(qreal radius);
|
||||||
|
void setFontSize(qreal size);
|
||||||
|
void setForegroundColor(const QColor &color);
|
||||||
|
void setHaloVisible(bool visible);
|
||||||
|
void setHasFixedRippleRadius(bool value);
|
||||||
|
void setIconPlacement(ui::ButtonIconPlacement placement);
|
||||||
|
void setOverlayColor(const QColor &color);
|
||||||
|
void setOverlayStyle(ui::OverlayStyle style);
|
||||||
|
void setRippleStyle(ui::RippleStyle style);
|
||||||
|
void setRole(ui::Role role);
|
||||||
|
|
||||||
|
QColor foregroundColor() const;
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
QColor overlayColor() const;
|
||||||
|
QColor disabledForegroundColor() const;
|
||||||
|
QColor disabledBackgroundColor() const;
|
||||||
|
|
||||||
|
qreal fontSize() const;
|
||||||
|
qreal cornerRadius() const;
|
||||||
|
qreal baseOpacity() const;
|
||||||
|
|
||||||
|
bool isHaloVisible() const;
|
||||||
|
bool hasFixedRippleRadius() const;
|
||||||
|
|
||||||
|
ui::Role role() const;
|
||||||
|
ui::OverlayStyle overlayStyle() const;
|
||||||
|
ui::RippleStyle rippleStyle() const;
|
||||||
|
ui::ButtonIconPlacement iconPlacement() const;
|
||||||
|
|
||||||
|
Qt::BGMode backgroundMode() const;
|
||||||
|
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum {
|
||||||
|
IconPadding = 12
|
||||||
|
};
|
||||||
|
|
||||||
|
void checkStateSet() override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
virtual void paintBackground(QPainter *painter);
|
||||||
|
virtual void paintHalo(QPainter *painter);
|
||||||
|
virtual void paintForeground(QPainter *painter);
|
||||||
|
virtual void updateClipPath();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
private:
|
||||||
|
RippleOverlay *ripple_overlay_;
|
||||||
|
FlatButtonStateMachine *state_machine_;
|
||||||
|
|
||||||
|
ui::Role role_;
|
||||||
|
ui::RippleStyle ripple_style_;
|
||||||
|
ui::ButtonIconPlacement icon_placement_;
|
||||||
|
ui::OverlayStyle overlay_style_;
|
||||||
|
|
||||||
|
Qt::BGMode bg_mode_;
|
||||||
|
|
||||||
|
QColor background_color_;
|
||||||
|
QColor foreground_color_;
|
||||||
|
QColor overlay_color_;
|
||||||
|
QColor disabled_color_;
|
||||||
|
QColor disabled_background_color_;
|
||||||
|
|
||||||
|
qreal fixed_ripple_radius_;
|
||||||
|
qreal corner_radius_;
|
||||||
|
qreal base_opacity_;
|
||||||
|
qreal font_size_;
|
||||||
|
|
||||||
|
bool use_fixed_ripple_radius_;
|
||||||
|
bool halo_visible_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UI_FLAT_BUTTON_H
|
23
include/ui/OverlayWidget.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef UI_OVERLAY_WIDGET_H
|
||||||
|
#define UI_OVERLAY_WIDGET_H
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class OverlayWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OverlayWidget(QWidget *parent = 0);
|
||||||
|
~OverlayWidget();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
|
QRect overlayGeometry() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UI_OVERLAY_WIDGET_H
|
31
include/ui/RaisedButton.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef UI_RAISED_BUTTON_H
|
||||||
|
#define UI_RAISED_BUTTON_H
|
||||||
|
|
||||||
|
#include <QGraphicsDropShadowEffect>
|
||||||
|
#include <QState>
|
||||||
|
#include <QStateMachine>
|
||||||
|
|
||||||
|
#include "FlatButton.h"
|
||||||
|
|
||||||
|
class RaisedButton : public FlatButton
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RaisedButton(QWidget *parent = 0);
|
||||||
|
explicit RaisedButton(const QString &text, QWidget *parent = 0);
|
||||||
|
~RaisedButton();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
QStateMachine *shadow_state_machine_;
|
||||||
|
QState *normal_state_;
|
||||||
|
QState *pressed_state_;
|
||||||
|
QGraphicsDropShadowEffect *effect_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UI_RAISED_BUTTON_H
|
136
include/ui/Ripple.h
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
#ifndef UI_RIPPLE_H
|
||||||
|
#define UI_RIPPLE_H
|
||||||
|
|
||||||
|
#include <QBrush>
|
||||||
|
#include <QEasingCurve>
|
||||||
|
#include <QParallelAnimationGroup>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
|
|
||||||
|
class RippleOverlay;
|
||||||
|
|
||||||
|
class Ripple : public QParallelAnimationGroup
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(qreal radius WRITE setRadius READ radius)
|
||||||
|
Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Ripple(const QPoint ¢er, QObject *parent = 0);
|
||||||
|
Ripple(const QPoint ¢er, RippleOverlay *overlay, QObject *parent = 0);
|
||||||
|
~Ripple();
|
||||||
|
|
||||||
|
inline void setOverlay(RippleOverlay *overlay);
|
||||||
|
|
||||||
|
void setRadius(qreal radius);
|
||||||
|
void setOpacity(qreal opacity);
|
||||||
|
void setColor(const QColor &color);
|
||||||
|
void setBrush(const QBrush &brush);
|
||||||
|
|
||||||
|
inline qreal radius() const;
|
||||||
|
inline qreal opacity() const;
|
||||||
|
inline QColor color() const;
|
||||||
|
inline QBrush brush() const;
|
||||||
|
inline QPoint center() const;
|
||||||
|
|
||||||
|
inline QPropertyAnimation *radiusAnimation() const;
|
||||||
|
inline QPropertyAnimation *opacityAnimation() const;
|
||||||
|
|
||||||
|
inline void setOpacityStartValue(qreal value);
|
||||||
|
inline void setOpacityEndValue(qreal value);
|
||||||
|
inline void setRadiusStartValue(qreal value);
|
||||||
|
inline void setRadiusEndValue(qreal value);
|
||||||
|
inline void setDuration(int msecs);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(Ripple)
|
||||||
|
|
||||||
|
QPropertyAnimation *animate(const QByteArray &property,
|
||||||
|
const QEasingCurve &easing = QEasingCurve::OutQuad,
|
||||||
|
int duration = 800);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
RippleOverlay *overlay_;
|
||||||
|
|
||||||
|
QPropertyAnimation *const radius_anim_;
|
||||||
|
QPropertyAnimation *const opacity_anim_;
|
||||||
|
|
||||||
|
qreal radius_;
|
||||||
|
qreal opacity_;
|
||||||
|
|
||||||
|
QPoint center_;
|
||||||
|
QBrush brush_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void Ripple::setOverlay(RippleOverlay *overlay)
|
||||||
|
{
|
||||||
|
overlay_ = overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal Ripple::radius() const
|
||||||
|
{
|
||||||
|
return radius_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal Ripple::opacity() const
|
||||||
|
{
|
||||||
|
return opacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QColor Ripple::color() const
|
||||||
|
{
|
||||||
|
return brush_.color();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QBrush Ripple::brush() const
|
||||||
|
{
|
||||||
|
return brush_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QPoint Ripple::center() const
|
||||||
|
{
|
||||||
|
return center_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QPropertyAnimation *Ripple::radiusAnimation() const
|
||||||
|
{
|
||||||
|
return radius_anim_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QPropertyAnimation *Ripple::opacityAnimation() const
|
||||||
|
{
|
||||||
|
return opacity_anim_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Ripple::setOpacityStartValue(qreal value)
|
||||||
|
{
|
||||||
|
opacity_anim_->setStartValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Ripple::setOpacityEndValue(qreal value)
|
||||||
|
{
|
||||||
|
opacity_anim_->setEndValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Ripple::setRadiusStartValue(qreal value)
|
||||||
|
{
|
||||||
|
radius_anim_->setStartValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Ripple::setRadiusEndValue(qreal value)
|
||||||
|
{
|
||||||
|
radius_anim_->setEndValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Ripple::setDuration(int msecs)
|
||||||
|
{
|
||||||
|
radius_anim_->setDuration(msecs);
|
||||||
|
opacity_anim_->setDuration(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UI_RIPPLE_H
|
58
include/ui/RippleOverlay.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef UI_RIPPLE_OVERLAY_H
|
||||||
|
#define UI_RIPPLE_OVERLAY_H
|
||||||
|
|
||||||
|
#include <QPainterPath>
|
||||||
|
|
||||||
|
#include "OverlayWidget.h"
|
||||||
|
|
||||||
|
class Ripple;
|
||||||
|
|
||||||
|
class RippleOverlay : public OverlayWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RippleOverlay(QWidget *parent = 0);
|
||||||
|
~RippleOverlay();
|
||||||
|
|
||||||
|
void addRipple(Ripple *ripple);
|
||||||
|
void addRipple(const QPoint &position, qreal radius = 300);
|
||||||
|
|
||||||
|
void removeRipple(Ripple *ripple);
|
||||||
|
|
||||||
|
inline void setClipping(bool enable);
|
||||||
|
inline bool hasClipping() const;
|
||||||
|
|
||||||
|
inline void setClipPath(const QPainterPath &path);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(RippleOverlay)
|
||||||
|
|
||||||
|
void paintRipple(QPainter *painter, Ripple *ripple);
|
||||||
|
|
||||||
|
QList<Ripple *> ripples_;
|
||||||
|
QPainterPath clip_path_;
|
||||||
|
bool use_clip_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void RippleOverlay::setClipping(bool enable)
|
||||||
|
{
|
||||||
|
use_clip_ = enable;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool RippleOverlay::hasClipping() const
|
||||||
|
{
|
||||||
|
return use_clip_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RippleOverlay::setClipPath(const QPainterPath &path)
|
||||||
|
{
|
||||||
|
clip_path_ = path;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UI_RIPPLE_OVERLAY_H
|
170
include/ui/TextField.h
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
#ifndef UI_TEXT_FIELD_H
|
||||||
|
#define UI_TEXT_FIELD_H
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
|
#include <QStateMachine>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
class TextField;
|
||||||
|
class TextFieldLabel;
|
||||||
|
class TextFieldStateMachine;
|
||||||
|
|
||||||
|
class TextField : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
|
||||||
|
Q_PROPERTY(QColor inkColor WRITE setInkColor READ inkColor)
|
||||||
|
Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ underlineColor)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TextField(QWidget *parent = 0);
|
||||||
|
~TextField();
|
||||||
|
|
||||||
|
void setInkColor(const QColor &color);
|
||||||
|
void setBackgroundColor(const QColor &color);
|
||||||
|
void setLabel(const QString &label);
|
||||||
|
void setLabelColor(const QColor &color);
|
||||||
|
void setLabelFontSize(qreal size);
|
||||||
|
void setShowLabel(bool value);
|
||||||
|
void setTextColor(const QColor &color);
|
||||||
|
void setUnderlineColor(const QColor &color);
|
||||||
|
|
||||||
|
QColor inkColor() const;
|
||||||
|
QColor labelColor() const;
|
||||||
|
QColor textColor() const;
|
||||||
|
QColor underlineColor() const;
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
QString label() const;
|
||||||
|
bool hasLabel() const;
|
||||||
|
qreal labelFontSize() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
QColor ink_color_;
|
||||||
|
QColor background_color_;
|
||||||
|
QColor label_color_;
|
||||||
|
QColor text_color_;
|
||||||
|
QColor underline_color_;
|
||||||
|
QString label_text_;
|
||||||
|
TextFieldLabel *label_;
|
||||||
|
TextFieldStateMachine *state_machine_;
|
||||||
|
bool show_label_;
|
||||||
|
qreal label_font_size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextFieldLabel : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(qreal scale WRITE setScale READ scale)
|
||||||
|
Q_PROPERTY(QPointF offset WRITE setOffset READ offset)
|
||||||
|
Q_PROPERTY(QColor color WRITE setColor READ color)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextFieldLabel(TextField *parent);
|
||||||
|
~TextFieldLabel();
|
||||||
|
|
||||||
|
inline void setColor(const QColor &color);
|
||||||
|
inline void setOffset(const QPointF &pos);
|
||||||
|
inline void setScale(qreal scale);
|
||||||
|
|
||||||
|
inline QColor color() const;
|
||||||
|
inline QPointF offset() const;
|
||||||
|
inline qreal scale() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextField *const text_field_;
|
||||||
|
|
||||||
|
QColor color_;
|
||||||
|
qreal scale_;
|
||||||
|
qreal x_;
|
||||||
|
qreal y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void TextFieldLabel::setColor(const QColor &color)
|
||||||
|
{
|
||||||
|
color_ = color;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TextFieldLabel::setOffset(const QPointF &pos)
|
||||||
|
{
|
||||||
|
x_ = pos.x();
|
||||||
|
y_ = pos.y();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void TextFieldLabel::setScale(qreal scale)
|
||||||
|
{
|
||||||
|
scale_ = scale;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QPointF TextFieldLabel::offset() const
|
||||||
|
{
|
||||||
|
return QPointF(x_, y_);
|
||||||
|
}
|
||||||
|
inline qreal TextFieldLabel::scale() const
|
||||||
|
{
|
||||||
|
return scale_;
|
||||||
|
}
|
||||||
|
inline QColor TextFieldLabel::color() const
|
||||||
|
{
|
||||||
|
return color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextFieldStateMachine : public QStateMachine
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(qreal progress WRITE setProgress READ progress)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextFieldStateMachine(TextField *parent);
|
||||||
|
~TextFieldStateMachine();
|
||||||
|
|
||||||
|
inline void setProgress(qreal progress);
|
||||||
|
void setLabel(TextFieldLabel *label);
|
||||||
|
|
||||||
|
inline qreal progress() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setupProperties();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPropertyAnimation *color_anim_;
|
||||||
|
QPropertyAnimation *offset_anim_;
|
||||||
|
|
||||||
|
QState *focused_state_;
|
||||||
|
QState *normal_state_;
|
||||||
|
|
||||||
|
TextField *text_field_;
|
||||||
|
TextFieldLabel *label_;
|
||||||
|
|
||||||
|
qreal progress_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void TextFieldStateMachine::setProgress(qreal progress)
|
||||||
|
{
|
||||||
|
progress_ = progress;
|
||||||
|
text_field_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline qreal TextFieldStateMachine::progress() const
|
||||||
|
{
|
||||||
|
return progress_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UI_TEXT_FIELD_H
|
89
include/ui/Theme.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#ifndef UI_THEME_H
|
||||||
|
#define UI_THEME_H
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
enum AvatarType {
|
||||||
|
Icon,
|
||||||
|
Image,
|
||||||
|
Letter
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default font size.
|
||||||
|
const int FontSize = 16;
|
||||||
|
|
||||||
|
// Default avatar size. Width and height.
|
||||||
|
const int AvatarSize = 40;
|
||||||
|
|
||||||
|
enum ButtonPreset {
|
||||||
|
FlatPreset,
|
||||||
|
CheckablePreset
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RippleStyle {
|
||||||
|
CenteredRipple,
|
||||||
|
PositionedRipple,
|
||||||
|
NoRipple
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OverlayStyle {
|
||||||
|
NoOverlay,
|
||||||
|
TintedOverlay,
|
||||||
|
GrayOverlay
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Role {
|
||||||
|
Default,
|
||||||
|
Primary,
|
||||||
|
Secondary
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ButtonIconPlacement {
|
||||||
|
LeftIcon,
|
||||||
|
RightIcon
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ProgressType {
|
||||||
|
DeterminateProgress,
|
||||||
|
IndeterminateProgress
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
Black,
|
||||||
|
BrightWhite,
|
||||||
|
FadedWhite,
|
||||||
|
MediumWhite,
|
||||||
|
DarkGreen,
|
||||||
|
LightGreen,
|
||||||
|
BrightGreen,
|
||||||
|
Gray,
|
||||||
|
Red,
|
||||||
|
Blue,
|
||||||
|
Transparent
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
|
||||||
|
class Theme : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Theme(QObject *parent = 0);
|
||||||
|
~Theme();
|
||||||
|
|
||||||
|
QColor getColor(const QString &key) const;
|
||||||
|
|
||||||
|
void setColor(const QString &key, const QColor &color);
|
||||||
|
void setColor(const QString &key, ui::Color &color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QColor rgba(int r, int g, int b, qreal a) const;
|
||||||
|
|
||||||
|
QHash<QString, QColor> colors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UI_THEME_H
|
33
include/ui/ThemeManager.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef UI_THEME_MANAGER_H
|
||||||
|
#define UI_THEME_MANAGER_H
|
||||||
|
|
||||||
|
#include <QCommonStyle>
|
||||||
|
|
||||||
|
#include "Theme.h"
|
||||||
|
|
||||||
|
class ThemeManager : public QCommonStyle
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline static ThemeManager &instance();
|
||||||
|
|
||||||
|
void setTheme(Theme *theme);
|
||||||
|
QColor themeColor(const QString &key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThemeManager();
|
||||||
|
|
||||||
|
ThemeManager(ThemeManager const &);
|
||||||
|
void operator=(ThemeManager const &);
|
||||||
|
|
||||||
|
Theme *theme_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline ThemeManager &ThemeManager::instance()
|
||||||
|
{
|
||||||
|
static ThemeManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UI_THEME_MANAGER_H
|
202
resources/fonts/LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
BIN
resources/fonts/OpenSans-Bold.ttf
Normal file
BIN
resources/fonts/OpenSans-BoldItalic.ttf
Normal file
BIN
resources/fonts/OpenSans-ExtraBold.ttf
Normal file
BIN
resources/fonts/OpenSans-ExtraBoldItalic.ttf
Normal file
BIN
resources/fonts/OpenSans-Italic.ttf
Normal file
BIN
resources/fonts/OpenSans-Light.ttf
Normal file
BIN
resources/fonts/OpenSans-LightItalic.ttf
Normal file
BIN
resources/fonts/OpenSans-Regular.ttf
Normal file
BIN
resources/fonts/OpenSans-Semibold.ttf
Normal file
BIN
resources/fonts/OpenSans-SemiboldItalic.ttf
Normal file
BIN
resources/icons/add-file.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/clip-dark.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/cog.png
Normal file
After Width: | Height: | Size: 952 B |
BIN
resources/icons/left-angle.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
resources/icons/left-chevron.png
Normal file
After Width: | Height: | Size: 590 B |
BIN
resources/icons/plus-symbol.png
Normal file
After Width: | Height: | Size: 453 B |
BIN
resources/icons/search.png
Normal file
After Width: | Height: | Size: 939 B |
BIN
resources/icons/send-button.png
Normal file
After Width: | Height: | Size: 799 B |
BIN
resources/icons/share-dark.png
Normal file
After Width: | Height: | Size: 799 B |
BIN
resources/icons/user-shape.png
Normal file
After Width: | Height: | Size: 730 B |
27
resources/res.qrc
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/icons">
|
||||||
|
<file>icons/left-chevron.png</file>
|
||||||
|
<file>icons/left-angle.png</file>
|
||||||
|
<file>icons/add-file.png</file>
|
||||||
|
<file>icons/send-button.png</file>
|
||||||
|
<file>icons/cog.png</file>
|
||||||
|
<file>icons/search.png</file>
|
||||||
|
<file>icons/plus-symbol.png</file>
|
||||||
|
<file>icons/clip-dark.png</file>
|
||||||
|
<file>icons/share-dark.png</file>
|
||||||
|
<file>icons/user-shape.png</file>
|
||||||
|
</qresource>
|
||||||
|
|
||||||
|
<qresource prefix="/fonts">
|
||||||
|
<file>fonts/OpenSans-Light.ttf</file>
|
||||||
|
<file>fonts/OpenSans-LightItalic.ttf</file>
|
||||||
|
<file>fonts/OpenSans-Regular.ttf</file>
|
||||||
|
<file>fonts/OpenSans-Italic.ttf</file>
|
||||||
|
<file>fonts/OpenSans-Bold.ttf</file>
|
||||||
|
<file>fonts/OpenSans-BoldItalic.ttf</file>
|
||||||
|
<file>fonts/OpenSans-Semibold.ttf</file>
|
||||||
|
<file>fonts/OpenSans-SemiboldItalic.ttf</file>
|
||||||
|
<file>fonts/OpenSans-ExtraBold.ttf</file>
|
||||||
|
<file>fonts/OpenSans-ExtraBoldItalic.ttf</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
277
src/ChatPage.cc
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui_ChatPage.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "ChatPage.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
#include "UserInfoWidget.h"
|
||||||
|
|
||||||
|
ChatPage::ChatPage(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::ChatPage)
|
||||||
|
, sync_interval_(2000)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
matrix_client_ = new MatrixClient("matrix.org", parent);
|
||||||
|
content_downloader_ = new QNetworkAccessManager(parent);
|
||||||
|
|
||||||
|
room_list_ = new RoomList(this);
|
||||||
|
|
||||||
|
top_bar_ = new TopRoomBar(this);
|
||||||
|
ui->topBarLayout->addWidget(top_bar_);
|
||||||
|
|
||||||
|
view_manager_ = new HistoryViewManager(this);
|
||||||
|
ui->mainContentLayout->addWidget(view_manager_);
|
||||||
|
|
||||||
|
text_input_ = new TextInputWidget(this);
|
||||||
|
ui->contentLayout->addWidget(text_input_);
|
||||||
|
|
||||||
|
user_info_widget_ = new UserInfoWidget(ui->sideBarTopWidget);
|
||||||
|
|
||||||
|
connect(room_list_,
|
||||||
|
SIGNAL(roomChanged(const RoomInfo &)),
|
||||||
|
this,
|
||||||
|
SLOT(changeTopRoomInfo(const RoomInfo &)));
|
||||||
|
|
||||||
|
connect(room_list_,
|
||||||
|
SIGNAL(roomChanged(const RoomInfo &)),
|
||||||
|
view_manager_,
|
||||||
|
SLOT(setHistoryView(const RoomInfo &)));
|
||||||
|
|
||||||
|
connect(room_list_,
|
||||||
|
SIGNAL(fetchRoomAvatar(const QString &, const QUrl &)),
|
||||||
|
this,
|
||||||
|
SLOT(fetchRoomAvatar(const QString &, const QUrl &)));
|
||||||
|
|
||||||
|
connect(text_input_,
|
||||||
|
SIGNAL(sendTextMessage(const QString &)),
|
||||||
|
this,
|
||||||
|
SLOT(sendTextMessage(const QString &)));
|
||||||
|
|
||||||
|
ui->sideBarTopUserInfoLayout->addWidget(user_info_widget_);
|
||||||
|
ui->sideBarMainLayout->addWidget(room_list_);
|
||||||
|
|
||||||
|
connect(matrix_client_,
|
||||||
|
SIGNAL(initialSyncCompleted(SyncResponse)),
|
||||||
|
this,
|
||||||
|
SLOT(initialSyncCompleted(SyncResponse)));
|
||||||
|
connect(matrix_client_,
|
||||||
|
SIGNAL(syncCompleted(SyncResponse)),
|
||||||
|
this,
|
||||||
|
SLOT(syncCompleted(SyncResponse)));
|
||||||
|
connect(matrix_client_,
|
||||||
|
SIGNAL(getOwnProfileResponse(QUrl, QString)),
|
||||||
|
this,
|
||||||
|
SLOT(updateOwnProfileInfo(QUrl, QString)));
|
||||||
|
connect(matrix_client_,
|
||||||
|
SIGNAL(messageSent(QString, int)),
|
||||||
|
this,
|
||||||
|
SLOT(messageSent(QString, int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::messageSent(QString event_id, int txn_id)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event_id);
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("client/transaction_id", txn_id + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::sendTextMessage(const QString &msg)
|
||||||
|
{
|
||||||
|
auto room = current_room_;
|
||||||
|
matrix_client_->sendTextMessage(current_room_.id(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
||||||
|
{
|
||||||
|
Q_UNUSED(userid);
|
||||||
|
|
||||||
|
matrix_client_->setServer(homeserver);
|
||||||
|
matrix_client_->setAccessToken(token);
|
||||||
|
|
||||||
|
matrix_client_->getOwnProfile();
|
||||||
|
matrix_client_->initialSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::startSync()
|
||||||
|
{
|
||||||
|
matrix_client_->sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::setOwnAvatar(QByteArray img)
|
||||||
|
{
|
||||||
|
if (img.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QPixmap pixmap;
|
||||||
|
pixmap.loadFromData(img);
|
||||||
|
user_info_widget_->setAvatar(pixmap.toImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::syncCompleted(SyncResponse response)
|
||||||
|
{
|
||||||
|
matrix_client_->setNextBatchToken(response.nextBatch());
|
||||||
|
|
||||||
|
/* room_list_->sync(response.rooms()); */
|
||||||
|
view_manager_->sync(response.rooms());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::initialSyncCompleted(SyncResponse response)
|
||||||
|
{
|
||||||
|
if (!response.nextBatch().isEmpty())
|
||||||
|
matrix_client_->setNextBatchToken(response.nextBatch());
|
||||||
|
|
||||||
|
view_manager_->initialize(response.rooms());
|
||||||
|
room_list_->setInitialRooms(response.rooms());
|
||||||
|
|
||||||
|
sync_timer_ = new QTimer(this);
|
||||||
|
connect(sync_timer_, SIGNAL(timeout()), this, SLOT(startSync()));
|
||||||
|
sync_timer_->start(sync_interval_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This function should be part of the matrix client for generic media retrieval.
|
||||||
|
void ChatPage::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
|
||||||
|
{
|
||||||
|
// TODO: move this into a Utils function
|
||||||
|
QList<QString> url_parts = avatar_url.toString().split("mxc://");
|
||||||
|
|
||||||
|
if (url_parts.size() != 2) {
|
||||||
|
qDebug() << "Invalid format for room avatar " << avatar_url.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString media_params = url_parts[1];
|
||||||
|
QString media_url = QString("%1/_matrix/media/r0/download/%2")
|
||||||
|
.arg(matrix_client_->getHomeServer(), media_params);
|
||||||
|
|
||||||
|
QNetworkRequest avatar_request(media_url);
|
||||||
|
QNetworkReply *reply = content_downloader_->get(avatar_request);
|
||||||
|
reply->setProperty("media_params", media_params);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, [this, media_params, roomid, reply]() {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
auto media = reply->property("media_params").toString();
|
||||||
|
|
||||||
|
if (media != media_params)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
qDebug() << reply->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status >= 400) {
|
||||||
|
qWarning() << "Request " << reply->request().url() << " returned " << status;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto img = reply->readAll();
|
||||||
|
|
||||||
|
if (img.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QPixmap pixmap;
|
||||||
|
pixmap.loadFromData(img);
|
||||||
|
room_avatars_.insert(roomid, pixmap);
|
||||||
|
|
||||||
|
this->room_list_->updateRoomAvatar(roomid, pixmap.toImage());
|
||||||
|
|
||||||
|
if (current_room_.id() == roomid) {
|
||||||
|
QIcon icon(pixmap);
|
||||||
|
this->top_bar_->updateRoomAvatar(icon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::updateOwnProfileInfo(QUrl avatar_url, QString display_name)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
auto userid = settings.value("auth/user_id").toString();
|
||||||
|
|
||||||
|
user_info_widget_->setUserId(userid);
|
||||||
|
user_info_widget_->setDisplayName(display_name);
|
||||||
|
|
||||||
|
// TODO: move this into a Utils function
|
||||||
|
QList<QString> url_parts = avatar_url.toString().split("mxc://");
|
||||||
|
|
||||||
|
if (url_parts.size() != 2) {
|
||||||
|
qDebug() << "Invalid format for media " << avatar_url.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString media_params = url_parts[1];
|
||||||
|
QString media_url = QString("%1/_matrix/media/r0/download/%2")
|
||||||
|
.arg(matrix_client_->getHomeServer(), media_params);
|
||||||
|
|
||||||
|
QNetworkRequest avatar_request(media_url);
|
||||||
|
QNetworkReply *reply = content_downloader_->get(avatar_request);
|
||||||
|
reply->setProperty("media_params", media_params);
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, [this, media_params, reply]() {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
auto media = reply->property("media_params").toString();
|
||||||
|
|
||||||
|
if (media != media_params)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
qDebug() << reply->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status >= 400) {
|
||||||
|
qWarning() << "Request " << reply->request().url() << " returned " << status;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOwnAvatar(reply->readAll());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatPage::changeTopRoomInfo(const RoomInfo &info)
|
||||||
|
{
|
||||||
|
top_bar_->updateRoomName(info.name());
|
||||||
|
top_bar_->updateRoomTopic(info.topic());
|
||||||
|
|
||||||
|
if (room_avatars_.contains(info.id())) {
|
||||||
|
QIcon icon(room_avatars_.value(info.id()));
|
||||||
|
top_bar_->updateRoomAvatar(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_room_ = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatPage::~ChatPage()
|
||||||
|
{
|
||||||
|
sync_timer_->stop();
|
||||||
|
delete ui;
|
||||||
|
}
|
31
src/Deserializable.cc
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
|
||||||
|
DeserializationException::DeserializationException(const std::string &msg) : msg_(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *DeserializationException::what() const throw()
|
||||||
|
{
|
||||||
|
return msg_.c_str();
|
||||||
|
}
|
145
src/HistoryView.cc
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QSpacerItem>
|
||||||
|
|
||||||
|
#include "HistoryView.h"
|
||||||
|
#include "HistoryViewItem.h"
|
||||||
|
|
||||||
|
const QList<QString> HistoryView::COLORS({"#FFF46E",
|
||||||
|
"#A58BFF",
|
||||||
|
"#50C9BA",
|
||||||
|
"#9EE6CF",
|
||||||
|
"#FFDD67",
|
||||||
|
"#2980B9",
|
||||||
|
"#FC993C",
|
||||||
|
"#2772DB",
|
||||||
|
"#CB8589",
|
||||||
|
"#DDE8B9",
|
||||||
|
"#55A44E",
|
||||||
|
"#A9EEE6",
|
||||||
|
"#53B759",
|
||||||
|
"#9E3997",
|
||||||
|
"#5D89D5",
|
||||||
|
"#BB86B7",
|
||||||
|
"#50a0cf",
|
||||||
|
"#3C989F",
|
||||||
|
"#5A4592",
|
||||||
|
"#235e5b",
|
||||||
|
"#d58247",
|
||||||
|
"#e0a729",
|
||||||
|
"#a2b636",
|
||||||
|
"#4BBE2E"});
|
||||||
|
|
||||||
|
HistoryView::HistoryView(QList<Event> events, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
addEvents(events);
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryView::HistoryView(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::sliderRangeChanged(int min, int max)
|
||||||
|
{
|
||||||
|
Q_UNUSED(min);
|
||||||
|
scroll_area_->verticalScrollBar()->setValue(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HistoryView::chooseRandomColor()
|
||||||
|
{
|
||||||
|
std::random_device random_device;
|
||||||
|
std::mt19937 engine{random_device()};
|
||||||
|
std::uniform_int_distribution<int> dist(0, HistoryView::COLORS.size() - 1);
|
||||||
|
|
||||||
|
return HistoryView::COLORS[dist(engine)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::addEvents(const QList<Event> &events)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < events.size(); i++) {
|
||||||
|
auto event = events[i];
|
||||||
|
|
||||||
|
if (event.type() == "m.room.message") {
|
||||||
|
auto msg_type = event.content().value("msgtype").toString();
|
||||||
|
|
||||||
|
if (msg_type == "m.text" || msg_type == "m.notice") {
|
||||||
|
auto with_sender = last_sender_ != event.sender();
|
||||||
|
auto color = nick_colors_.value(event.sender());
|
||||||
|
|
||||||
|
if (color.isEmpty()) {
|
||||||
|
color = chooseRandomColor();
|
||||||
|
nick_colors_.insert(event.sender(), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
addHistoryItem(event, color, with_sender);
|
||||||
|
last_sender_ = event.sender();
|
||||||
|
} else {
|
||||||
|
qDebug() << "Ignoring message" << msg_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::init()
|
||||||
|
{
|
||||||
|
top_layout_ = new QVBoxLayout(this);
|
||||||
|
top_layout_->setSpacing(0);
|
||||||
|
top_layout_->setMargin(0);
|
||||||
|
|
||||||
|
scroll_area_ = new QScrollArea(this);
|
||||||
|
scroll_area_->setWidgetResizable(true);
|
||||||
|
|
||||||
|
scroll_widget_ = new QWidget();
|
||||||
|
|
||||||
|
scroll_layout_ = new QVBoxLayout();
|
||||||
|
scroll_layout_->addStretch(1);
|
||||||
|
scroll_layout_->setSpacing(0);
|
||||||
|
|
||||||
|
scroll_widget_->setLayout(scroll_layout_);
|
||||||
|
|
||||||
|
scroll_area_->setWidget(scroll_widget_);
|
||||||
|
|
||||||
|
top_layout_->addWidget(scroll_area_);
|
||||||
|
|
||||||
|
setLayout(top_layout_);
|
||||||
|
|
||||||
|
connect(scroll_area_->verticalScrollBar(),
|
||||||
|
SIGNAL(rangeChanged(int, int)),
|
||||||
|
this,
|
||||||
|
SLOT(sliderRangeChanged(int, int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::addHistoryItem(Event event, QString color, bool with_sender)
|
||||||
|
{
|
||||||
|
// TODO: Probably create another function instead of passing the flag.
|
||||||
|
HistoryViewItem *item = new HistoryViewItem(event, with_sender, color, scroll_widget_);
|
||||||
|
scroll_layout_->addWidget(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryView::~HistoryView()
|
||||||
|
{
|
||||||
|
}
|
80
src/HistoryViewItem.cc
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "HistoryViewItem.h"
|
||||||
|
|
||||||
|
HistoryViewItem::HistoryViewItem(Event event, bool with_sender, QString color, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
QString sender = "";
|
||||||
|
|
||||||
|
if (with_sender)
|
||||||
|
sender = event.sender().split(":")[0].split("@")[1];
|
||||||
|
|
||||||
|
auto body = event.content().value("body").toString();
|
||||||
|
|
||||||
|
auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp());
|
||||||
|
auto local_time = timestamp.toString("HH:mm");
|
||||||
|
|
||||||
|
time_label_ = new QLabel(this);
|
||||||
|
time_label_->setWordWrap(true);
|
||||||
|
QString msg(
|
||||||
|
"<html>"
|
||||||
|
"<head/>"
|
||||||
|
"<body>"
|
||||||
|
" <span style=\"font-size: 7pt; color: #5d6565;\">"
|
||||||
|
" %1"
|
||||||
|
" </span>"
|
||||||
|
"</body>"
|
||||||
|
"</html>");
|
||||||
|
time_label_->setText(msg.arg(local_time));
|
||||||
|
time_label_->setStyleSheet("margin-left: 7px; margin-right: 7px; margin-top: 0;");
|
||||||
|
time_label_->setAlignment(Qt::AlignTop);
|
||||||
|
|
||||||
|
content_label_ = new QLabel(this);
|
||||||
|
content_label_->setWordWrap(true);
|
||||||
|
content_label_->setAlignment(Qt::AlignTop);
|
||||||
|
content_label_->setStyleSheet("margin: 0;");
|
||||||
|
QString content(
|
||||||
|
"<html>"
|
||||||
|
"<head/>"
|
||||||
|
"<body>"
|
||||||
|
" <span style=\"font-size: 10pt; font-weight: 600; color: %1\">"
|
||||||
|
" %2"
|
||||||
|
" </span>"
|
||||||
|
" <span style=\"font-size: 10pt;\">"
|
||||||
|
" %3"
|
||||||
|
" </span>"
|
||||||
|
"</body>"
|
||||||
|
"</html>");
|
||||||
|
content_label_->setText(content.arg(color).arg(sender).arg(body));
|
||||||
|
|
||||||
|
top_layout_ = new QHBoxLayout();
|
||||||
|
top_layout_->setMargin(0);
|
||||||
|
|
||||||
|
top_layout_->addWidget(time_label_);
|
||||||
|
top_layout_->addWidget(content_label_, 1);
|
||||||
|
|
||||||
|
setLayout(top_layout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryViewItem::~HistoryViewItem()
|
||||||
|
{
|
||||||
|
}
|
83
src/HistoryViewManager.cc
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "HistoryView.h"
|
||||||
|
#include "HistoryViewManager.h"
|
||||||
|
|
||||||
|
HistoryViewManager::HistoryViewManager(QWidget *parent)
|
||||||
|
: QStackedWidget(parent)
|
||||||
|
{
|
||||||
|
setStyleSheet(
|
||||||
|
"QWidget {background: #171919; color: #ebebeb;}"
|
||||||
|
"QScrollBar:vertical { background-color: #171919; width: 10px; border-radius: 20px; margin: 0px 2px 0 2px; }"
|
||||||
|
"QScrollBar::handle:vertical { border-radius : 50px; background-color : #1c3133; }"
|
||||||
|
"QScrollBar::add-line:vertical { border: none; background: none; }"
|
||||||
|
"QScrollBar::sub-line:vertical { border: none; background: none; }");
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryViewManager::~HistoryViewManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryViewManager::initialize(const Rooms &rooms)
|
||||||
|
{
|
||||||
|
for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) {
|
||||||
|
auto roomid = it.key();
|
||||||
|
auto events = it.value().timeline().events();
|
||||||
|
|
||||||
|
// Create a history view with the room events.
|
||||||
|
HistoryView *view = new HistoryView(events);
|
||||||
|
views_.insert(it.key(), view);
|
||||||
|
|
||||||
|
// Add the view in the widget stack.
|
||||||
|
addWidget(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryViewManager::sync(const Rooms &rooms)
|
||||||
|
{
|
||||||
|
for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) {
|
||||||
|
auto roomid = it.key();
|
||||||
|
|
||||||
|
if (!views_.contains(roomid)) {
|
||||||
|
qDebug() << "Ignoring event from unknown room";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto view = views_.value(roomid);
|
||||||
|
auto events = it.value().timeline().events();
|
||||||
|
|
||||||
|
view->addEvents(events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryViewManager::setHistoryView(const RoomInfo &info)
|
||||||
|
{
|
||||||
|
if (!views_.contains(info.id())) {
|
||||||
|
qDebug() << "Room List id is not present in view manager";
|
||||||
|
qDebug() << info.name();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto widget = views_.value(info.id());
|
||||||
|
|
||||||
|
setCurrentWidget(widget);
|
||||||
|
}
|
31
src/InputValidator.cc
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "InputValidator.h"
|
||||||
|
|
||||||
|
// FIXME: Maybe change the regex to match the real Matrix ID format and not email.
|
||||||
|
InputValidator::InputValidator(QObject *parent)
|
||||||
|
: matrix_id_("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}")
|
||||||
|
, matrix_localpart_("[A-za-z0-9._%+-]{3,}")
|
||||||
|
, matrix_password_(".{8,}")
|
||||||
|
, server_domain_("(?!\\-)(?:[a-zA-Z\\d\\-]{0,62}[a-zA-Z\\d]\\.){1,126}(?!\\d+)[a-zA-Z\\d]{1,63}")
|
||||||
|
{
|
||||||
|
id_ = new QRegExpValidator(matrix_id_, parent);
|
||||||
|
localpart_ = new QRegExpValidator(matrix_localpart_, parent);
|
||||||
|
password_ = new QRegExpValidator(matrix_password_, parent);
|
||||||
|
domain_ = new QRegExpValidator(server_domain_, parent);
|
||||||
|
}
|
89
src/Login.cc
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
#include "Login.h"
|
||||||
|
|
||||||
|
LoginRequest::LoginRequest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginRequest::LoginRequest(QString username, QString password)
|
||||||
|
: user_(username)
|
||||||
|
, password_(password)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray LoginRequest::serialize()
|
||||||
|
{
|
||||||
|
QJsonObject body{
|
||||||
|
{"type", "m.login.password"},
|
||||||
|
{"user", user_},
|
||||||
|
{"password", password_}};
|
||||||
|
|
||||||
|
return QJsonDocument(body).toJson(QJsonDocument::Compact);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginRequest::setPassword(QString password)
|
||||||
|
{
|
||||||
|
password_ = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginRequest::setUser(QString username)
|
||||||
|
{
|
||||||
|
user_ = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoginResponse::getAccessToken()
|
||||||
|
{
|
||||||
|
return access_token_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoginResponse::getHomeServer()
|
||||||
|
{
|
||||||
|
return home_server_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LoginResponse::getUserId()
|
||||||
|
{
|
||||||
|
return user_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginResponse::deserialize(QJsonDocument data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("Login response is not a JSON object");
|
||||||
|
|
||||||
|
QJsonObject object = data.object();
|
||||||
|
|
||||||
|
if (object.value("access_token") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Login: missing access_token param");
|
||||||
|
|
||||||
|
if (object.value("home_server") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Login: missing home_server param");
|
||||||
|
|
||||||
|
if (object.value("user_id") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Login: missing user_id param");
|
||||||
|
|
||||||
|
access_token_ = object.value("access_token").toString();
|
||||||
|
home_server_ = object.value("home_server").toString();
|
||||||
|
user_id_ = object.value("user_id").toString();
|
||||||
|
}
|
147
src/LoginPage.cc
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "LoginPage.h"
|
||||||
|
|
||||||
|
LoginPage::LoginPage(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, matrix_id_validator_(new InputValidator(parent))
|
||||||
|
{
|
||||||
|
top_layout_ = new QVBoxLayout();
|
||||||
|
|
||||||
|
back_layout_ = new QHBoxLayout();
|
||||||
|
back_layout_->setSpacing(0);
|
||||||
|
back_layout_->setContentsMargins(5, 5, -1, -1);
|
||||||
|
|
||||||
|
back_button_ = new FlatButton(this);
|
||||||
|
back_button_->setMinimumSize(QSize(30, 30));
|
||||||
|
back_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
|
||||||
|
back_button_->setIcon(icon);
|
||||||
|
back_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
|
back_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
back_layout_->addStretch(1);
|
||||||
|
|
||||||
|
logo_layout_ = new QHBoxLayout();
|
||||||
|
logo_layout_->setContentsMargins(0, 20, 0, 20);
|
||||||
|
logo_ = new QLabel(this);
|
||||||
|
logo_->setText("nheko");
|
||||||
|
logo_->setStyleSheet("font-size: 22pt; font-weight: 400;");
|
||||||
|
logo_layout_->addWidget(logo_, 0, Qt::AlignHCenter);
|
||||||
|
|
||||||
|
form_wrapper_ = new QHBoxLayout();
|
||||||
|
form_widget_ = new QWidget();
|
||||||
|
form_widget_->setMinimumSize(QSize(350, 200));
|
||||||
|
|
||||||
|
form_layout_ = new QVBoxLayout();
|
||||||
|
form_layout_->setSpacing(20);
|
||||||
|
form_layout_->setContentsMargins(0, 00, 0, 30);
|
||||||
|
form_widget_->setLayout(form_layout_);
|
||||||
|
|
||||||
|
form_wrapper_->addStretch(1);
|
||||||
|
form_wrapper_->addWidget(form_widget_);
|
||||||
|
form_wrapper_->addStretch(1);
|
||||||
|
|
||||||
|
username_input_ = new TextField();
|
||||||
|
username_input_->setLabel("Username");
|
||||||
|
username_input_->setInkColor("#577275");
|
||||||
|
username_input_->setBackgroundColor("#f9f9f9");
|
||||||
|
|
||||||
|
password_input_ = new TextField();
|
||||||
|
password_input_->setLabel("Password");
|
||||||
|
password_input_->setInkColor("#577275");
|
||||||
|
password_input_->setBackgroundColor("#f9f9f9");
|
||||||
|
password_input_->setEchoMode(QLineEdit::Password);
|
||||||
|
|
||||||
|
form_layout_->addWidget(username_input_, Qt::AlignHCenter, 0);
|
||||||
|
form_layout_->addWidget(password_input_, Qt::AlignHCenter, 0);
|
||||||
|
|
||||||
|
button_layout_ = new QHBoxLayout();
|
||||||
|
button_layout_->setSpacing(0);
|
||||||
|
button_layout_->setContentsMargins(0, 0, 0, 50);
|
||||||
|
|
||||||
|
login_button_ = new RaisedButton("LOGIN", this);
|
||||||
|
login_button_->setBackgroundColor(QColor("#171919"));
|
||||||
|
login_button_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
login_button_->setMinimumSize(350, 65);
|
||||||
|
login_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
login_button_->setFontSize(17);
|
||||||
|
login_button_->setCornerRadius(3);
|
||||||
|
|
||||||
|
button_layout_->addStretch(1);
|
||||||
|
button_layout_->addWidget(login_button_);
|
||||||
|
button_layout_->addStretch(1);
|
||||||
|
|
||||||
|
error_label_ = new QLabel(this);
|
||||||
|
error_label_->setStyleSheet("color: #E22826; font-size: 11pt;");
|
||||||
|
|
||||||
|
top_layout_->addLayout(back_layout_);
|
||||||
|
top_layout_->addStretch(1);
|
||||||
|
top_layout_->addLayout(logo_layout_);
|
||||||
|
top_layout_->addLayout(form_wrapper_);
|
||||||
|
top_layout_->addStretch(1);
|
||||||
|
top_layout_->addLayout(button_layout_);
|
||||||
|
top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
|
||||||
|
top_layout_->addStretch(1);
|
||||||
|
|
||||||
|
connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
|
||||||
|
connect(login_button_, SIGNAL(clicked()), this, SLOT(onLoginButtonClicked()));
|
||||||
|
connect(username_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
||||||
|
connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
|
||||||
|
|
||||||
|
username_input_->setValidator(matrix_id_validator_->id_);
|
||||||
|
|
||||||
|
setLayout(top_layout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginPage::loginError(QString error)
|
||||||
|
{
|
||||||
|
qWarning() << "Error Message: " << error;
|
||||||
|
error_label_->setText(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginPage::onLoginButtonClicked()
|
||||||
|
{
|
||||||
|
error_label_->setText("");
|
||||||
|
|
||||||
|
if (!username_input_->hasAcceptableInput()) {
|
||||||
|
loginError("Invalid Matrix ID");
|
||||||
|
} else if (password_input_->text().isEmpty()) {
|
||||||
|
loginError("Empty password");
|
||||||
|
} else {
|
||||||
|
QString user = username_input_->text().split("@").at(0);
|
||||||
|
QString home_server = username_input_->text().split("@").at(1);
|
||||||
|
QString password = password_input_->text();
|
||||||
|
|
||||||
|
emit userLogin(user, password, home_server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginPage::onBackButtonClicked()
|
||||||
|
{
|
||||||
|
emit backButtonClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginPage::~LoginPage()
|
||||||
|
{
|
||||||
|
}
|
134
src/MainWindow.cc
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MainWindow.h"
|
||||||
|
#include "ui_MainWindow.h"
|
||||||
|
|
||||||
|
#include <QLayout>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui_(new Ui::MainWindow)
|
||||||
|
, welcome_page_(new WelcomePage(parent))
|
||||||
|
, login_page_(new LoginPage(parent))
|
||||||
|
, register_page_(new RegisterPage(parent))
|
||||||
|
, chat_page_(new ChatPage(parent))
|
||||||
|
, matrix_client_(new MatrixClient("matrix.org", parent))
|
||||||
|
{
|
||||||
|
ui_->setupUi(this);
|
||||||
|
|
||||||
|
// Initialize sliding widget manager.
|
||||||
|
sliding_stack_ = new SlidingStackWidget(this);
|
||||||
|
sliding_stack_->addWidget(welcome_page_);
|
||||||
|
sliding_stack_->addWidget(login_page_);
|
||||||
|
sliding_stack_->addWidget(register_page_);
|
||||||
|
sliding_stack_->addWidget(chat_page_);
|
||||||
|
|
||||||
|
setCentralWidget(sliding_stack_);
|
||||||
|
|
||||||
|
connect(welcome_page_, SIGNAL(userLogin()), this, SLOT(showLoginPage()));
|
||||||
|
connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage()));
|
||||||
|
|
||||||
|
connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
|
||||||
|
connect(login_page_,
|
||||||
|
SIGNAL(userLogin(const QString &, const QString &, const QString &)),
|
||||||
|
this,
|
||||||
|
SLOT(matrixLogin(const QString &, const QString &, const QString &)));
|
||||||
|
|
||||||
|
connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
|
||||||
|
connect(register_page_,
|
||||||
|
SIGNAL(registerUser(const QString &, const QString &, const QString &)),
|
||||||
|
this,
|
||||||
|
SLOT(matrixRegister(const QString &, const QString &, const QString &)));
|
||||||
|
|
||||||
|
connect(matrix_client_, SIGNAL(loginError(QString)), login_page_, SLOT(loginError(QString)));
|
||||||
|
connect(matrix_client_,
|
||||||
|
SIGNAL(loginSuccess(QString, QString, QString)),
|
||||||
|
this,
|
||||||
|
SLOT(showChatPage(QString, QString, QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::matrixLogin(const QString &username, const QString &password, const QString &home_server)
|
||||||
|
{
|
||||||
|
qDebug() << "About to login into Matrix";
|
||||||
|
qDebug() << "Userame: " << username;
|
||||||
|
|
||||||
|
matrix_client_->setServer(home_server);
|
||||||
|
matrix_client_->login(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showChatPage(QString userid, QString homeserver, QString token)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("auth/access_token", token);
|
||||||
|
settings.setValue("auth/home_server", homeserver);
|
||||||
|
settings.setValue("auth/user_id", userid);
|
||||||
|
|
||||||
|
int index = sliding_stack_->getWidgetIndex(chat_page_);
|
||||||
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
||||||
|
|
||||||
|
chat_page_->bootstrap(userid, homeserver, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::matrixRegister(const QString &username, const QString &password, const QString &server)
|
||||||
|
{
|
||||||
|
qDebug() << "About to register to Matrix";
|
||||||
|
qDebug() << "Username: " << username << " Password: " << password << " Server: " << server;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showWelcomePage()
|
||||||
|
{
|
||||||
|
int index = sliding_stack_->getWidgetIndex(welcome_page_);
|
||||||
|
|
||||||
|
if (sliding_stack_->currentIndex() == sliding_stack_->getWidgetIndex(login_page_))
|
||||||
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::RIGHT_TO_LEFT);
|
||||||
|
else
|
||||||
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showLoginPage()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
|
||||||
|
if (settings.contains("auth/access_token") &&
|
||||||
|
settings.contains("auth/home_server") &&
|
||||||
|
settings.contains("auth/user_id")) {
|
||||||
|
QString token = settings.value("auth/access_token").toString();
|
||||||
|
QString home_server = settings.value("auth/home_server").toString();
|
||||||
|
QString user_id = settings.value("auth/user_id").toString();
|
||||||
|
|
||||||
|
showChatPage(user_id, home_server, token);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = sliding_stack_->getWidgetIndex(login_page_);
|
||||||
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::LEFT_TO_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showRegisterPage()
|
||||||
|
{
|
||||||
|
int index = sliding_stack_->getWidgetIndex(register_page_);
|
||||||
|
sliding_stack_->slideInIndex(index, SlidingStackWidget::AnimationDirection::RIGHT_TO_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui_;
|
||||||
|
}
|
365
src/MatrixClient.cc
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
|
||||||
|
#include "Login.h"
|
||||||
|
#include "MatrixClient.h"
|
||||||
|
#include "Profile.h"
|
||||||
|
|
||||||
|
MatrixClient::MatrixClient(QString server, QObject *parent)
|
||||||
|
: QNetworkAccessManager(parent)
|
||||||
|
{
|
||||||
|
server_ = "https://" + server;
|
||||||
|
api_url_ = "/_matrix/client/r0";
|
||||||
|
token_ = "";
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
txn_id_ = settings.value("client/transaction_id", 1).toInt();
|
||||||
|
|
||||||
|
// FIXME: Other QNetworkAccessManagers use the finish handler.
|
||||||
|
connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(onResponse(QNetworkReply *)));
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixClient::~MatrixClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onVersionsResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
qDebug() << "Handling the versions response";
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
qDebug() << json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onLoginResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status_code == 403) {
|
||||||
|
emit loginError("Wrong username or password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status_code == 404) {
|
||||||
|
emit loginError("Login endpoint was not found on the server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status_code != 200) {
|
||||||
|
qDebug() << "Login response: status code " << status_code;
|
||||||
|
|
||||||
|
if (status_code >= 400) {
|
||||||
|
qWarning() << "Login error: " << reply->errorString();
|
||||||
|
emit loginError("An unknown error occured. Please try again.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
LoginResponse response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response.deserialize(json);
|
||||||
|
emit loginSuccess(response.getUserId(),
|
||||||
|
response.getHomeServer(),
|
||||||
|
response.getAccessToken());
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << "Malformed JSON response" << e.what();
|
||||||
|
emit loginError("Malformed response. Possibly not a Matrix server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onRegisterResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
qDebug() << "Handling the register response";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onGetOwnProfileResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status >= 400) {
|
||||||
|
qWarning() << reply->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
ProfileResponse response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response.deserialize(json);
|
||||||
|
emit getOwnProfileResponse(response.getAvatarUrl(), response.getDisplayName());
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << "Profile malformed response" << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onInitialSyncResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status == 0 || status >= 400) {
|
||||||
|
qWarning() << reply->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
|
||||||
|
if (data.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
SyncResponse response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response.deserialize(json);
|
||||||
|
emit initialSyncCompleted(response);
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << "Sync malformed response" << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onSyncResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status == 0 || status >= 400) {
|
||||||
|
qWarning() << reply->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
|
||||||
|
if (data.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
SyncResponse response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response.deserialize(json);
|
||||||
|
emit syncCompleted(response);
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << "Sync malformed response" << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onSendTextMessageResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
|
if (status == 0 || status >= 400) {
|
||||||
|
qWarning() << reply->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
|
||||||
|
if (data.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
|
if (!json.isObject()) {
|
||||||
|
qDebug() << "Send message response is not a JSON object";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto object = json.object();
|
||||||
|
|
||||||
|
if (!object.contains("event_id")) {
|
||||||
|
qDebug() << "SendTextMessage: missnig event_id from response";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit messageSent(object.value("event_id").toString(),
|
||||||
|
reply->property("txn_id").toInt());
|
||||||
|
|
||||||
|
incrementTransactionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::onResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
switch (reply->property("endpoint").toInt()) {
|
||||||
|
case Endpoint::Versions:
|
||||||
|
onVersionsResponse(reply);
|
||||||
|
break;
|
||||||
|
case Endpoint::Login:
|
||||||
|
onLoginResponse(reply);
|
||||||
|
break;
|
||||||
|
case Endpoint::Register:
|
||||||
|
onRegisterResponse(reply);
|
||||||
|
case Endpoint::GetOwnProfile:
|
||||||
|
onGetOwnProfileResponse(reply);
|
||||||
|
case Endpoint::InitialSync:
|
||||||
|
onInitialSyncResponse(reply);
|
||||||
|
case Endpoint::Sync:
|
||||||
|
onSyncResponse(reply);
|
||||||
|
case Endpoint::SendTextMessage:
|
||||||
|
onSendTextMessageResponse(reply);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::login(const QString &username, const QString &password)
|
||||||
|
{
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath(api_url_ + "/login");
|
||||||
|
|
||||||
|
QNetworkRequest request(endpoint);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
LoginRequest body(username, password);
|
||||||
|
|
||||||
|
QNetworkReply *reply = post(request, body.serialize());
|
||||||
|
reply->setProperty("endpoint", Endpoint::Login);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::sync()
|
||||||
|
{
|
||||||
|
QJsonObject filter{{"room",
|
||||||
|
QJsonObject{{"ephemeral", QJsonObject{{"limit", 0}}}}},
|
||||||
|
{"presence", QJsonObject{{"limit", 0}}}};
|
||||||
|
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("set_presence", "online");
|
||||||
|
query.addQueryItem("filter", QJsonDocument(filter).toJson(QJsonDocument::Compact));
|
||||||
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
|
if (next_batch_.isEmpty()) {
|
||||||
|
qDebug() << "Sync requires a valid next_batch token. Initial sync should be performed.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.addQueryItem("since", next_batch_);
|
||||||
|
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath(api_url_ + "/sync");
|
||||||
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
|
|
||||||
|
QNetworkReply *reply = get(request);
|
||||||
|
reply->setProperty("endpoint", Endpoint::Sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::sendTextMessage(QString roomid, QString msg)
|
||||||
|
{
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath(api_url_ + QString("/rooms/%1/send/m.room.message/%2").arg(roomid).arg(txn_id_));
|
||||||
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
|
QJsonObject body{
|
||||||
|
{"msgtype", "m.text"},
|
||||||
|
{"body", msg}};
|
||||||
|
|
||||||
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
|
||||||
|
QNetworkReply *reply = put(request, QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
|
|
||||||
|
reply->setProperty("endpoint", Endpoint::SendTextMessage);
|
||||||
|
reply->setProperty("txn_id", txn_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::initialSync()
|
||||||
|
{
|
||||||
|
QJsonObject filter{{"room",
|
||||||
|
QJsonObject{{"timeline", QJsonObject{{"limit", 70}}},
|
||||||
|
{"ephemeral", QJsonObject{{"limit", 0}}}}},
|
||||||
|
{"presence", QJsonObject{{"limit", 0}}}};
|
||||||
|
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("full_state", "true");
|
||||||
|
query.addQueryItem("set_presence", "online");
|
||||||
|
query.addQueryItem("filter", QJsonDocument(filter).toJson(QJsonDocument::Compact));
|
||||||
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath(api_url_ + "/sync");
|
||||||
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
|
|
||||||
|
QNetworkReply *reply = get(request);
|
||||||
|
reply->setProperty("endpoint", Endpoint::InitialSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::versions()
|
||||||
|
{
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath("/_matrix/client/versions");
|
||||||
|
|
||||||
|
QNetworkRequest request(endpoint);
|
||||||
|
|
||||||
|
QNetworkReply *reply = get(request);
|
||||||
|
reply->setProperty("endpoint", Endpoint::Versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixClient::getOwnProfile()
|
||||||
|
{
|
||||||
|
// FIXME: Remove settings from the matrix client. The class should store the user's matrix ID.
|
||||||
|
QSettings settings;
|
||||||
|
auto userid = settings.value("auth/user_id", "").toString();
|
||||||
|
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("access_token", token_);
|
||||||
|
|
||||||
|
QUrl endpoint(server_);
|
||||||
|
endpoint.setPath(api_url_ + "/profile/" + userid);
|
||||||
|
endpoint.setQuery(query);
|
||||||
|
|
||||||
|
QNetworkRequest request(QString(endpoint.toEncoded()));
|
||||||
|
|
||||||
|
QNetworkReply *reply = get(request);
|
||||||
|
reply->setProperty("endpoint", Endpoint::GetOwnProfile);
|
||||||
|
}
|
50
src/Profile.cc
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
#include "Profile.h"
|
||||||
|
|
||||||
|
QUrl ProfileResponse::getAvatarUrl()
|
||||||
|
{
|
||||||
|
return avatar_url_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProfileResponse::getDisplayName()
|
||||||
|
{
|
||||||
|
return display_name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileResponse::deserialize(QJsonDocument data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("Profile response is not a JSON object");
|
||||||
|
|
||||||
|
QJsonObject object = data.object();
|
||||||
|
|
||||||
|
if (object.value("avatar_url") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Profile: missing avatar_url param");
|
||||||
|
|
||||||
|
if (object.value("displayname") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Profile: missing displayname param");
|
||||||
|
|
||||||
|
avatar_url_ = QUrl(object.value("avatar_url").toString());
|
||||||
|
display_name_ = object.value("displayname").toString();
|
||||||
|
}
|
166
src/RegisterPage.cc
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
|
#include "RegisterPage.h"
|
||||||
|
|
||||||
|
RegisterPage::RegisterPage(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, validator_(new InputValidator(parent))
|
||||||
|
{
|
||||||
|
top_layout_ = new QVBoxLayout();
|
||||||
|
|
||||||
|
back_layout_ = new QHBoxLayout();
|
||||||
|
back_layout_->setSpacing(0);
|
||||||
|
back_layout_->setContentsMargins(5, 5, -1, -1);
|
||||||
|
|
||||||
|
back_button_ = new FlatButton(this);
|
||||||
|
back_button_->setMinimumSize(QSize(30, 30));
|
||||||
|
back_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
|
||||||
|
back_button_->setIcon(icon);
|
||||||
|
back_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
|
back_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
back_layout_->addStretch(1);
|
||||||
|
|
||||||
|
logo_layout_ = new QHBoxLayout();
|
||||||
|
logo_layout_->setContentsMargins(0, 20, 0, 20);
|
||||||
|
logo_ = new QLabel(this);
|
||||||
|
logo_->setText("nheko");
|
||||||
|
logo_->setStyleSheet("font-size: 22pt; font-weight: 400;");
|
||||||
|
logo_layout_->addWidget(logo_, 0, Qt::AlignHCenter);
|
||||||
|
|
||||||
|
form_wrapper_ = new QHBoxLayout();
|
||||||
|
form_widget_ = new QWidget();
|
||||||
|
form_widget_->setMinimumSize(QSize(350, 300));
|
||||||
|
|
||||||
|
form_layout_ = new QVBoxLayout();
|
||||||
|
form_layout_->setSpacing(20);
|
||||||
|
form_layout_->setContentsMargins(0, 00, 0, 60);
|
||||||
|
form_widget_->setLayout(form_layout_);
|
||||||
|
|
||||||
|
form_wrapper_->addStretch(1);
|
||||||
|
form_wrapper_->addWidget(form_widget_);
|
||||||
|
form_wrapper_->addStretch(1);
|
||||||
|
|
||||||
|
username_input_ = new TextField();
|
||||||
|
username_input_->setLabel("Username");
|
||||||
|
username_input_->setInkColor("#577275");
|
||||||
|
username_input_->setBackgroundColor("#f9f9f9");
|
||||||
|
|
||||||
|
password_input_ = new TextField();
|
||||||
|
password_input_->setLabel("Password");
|
||||||
|
password_input_->setInkColor("#577275");
|
||||||
|
password_input_->setBackgroundColor("#f9f9f9");
|
||||||
|
password_input_->setEchoMode(QLineEdit::Password);
|
||||||
|
|
||||||
|
password_confirmation_ = new TextField();
|
||||||
|
password_confirmation_->setLabel("Password confirmation");
|
||||||
|
password_confirmation_->setInkColor("#577275");
|
||||||
|
password_confirmation_->setBackgroundColor("#f9f9f9");
|
||||||
|
password_confirmation_->setEchoMode(QLineEdit::Password);
|
||||||
|
|
||||||
|
server_input_ = new TextField();
|
||||||
|
server_input_->setLabel("Home Server");
|
||||||
|
server_input_->setInkColor("#577275");
|
||||||
|
server_input_->setBackgroundColor("#f9f9f9");
|
||||||
|
|
||||||
|
form_layout_->addWidget(username_input_, Qt::AlignHCenter, 0);
|
||||||
|
form_layout_->addWidget(password_input_, Qt::AlignHCenter, 0);
|
||||||
|
form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter, 0);
|
||||||
|
form_layout_->addWidget(server_input_, Qt::AlignHCenter, 0);
|
||||||
|
|
||||||
|
button_layout_ = new QHBoxLayout();
|
||||||
|
button_layout_->setSpacing(0);
|
||||||
|
button_layout_->setContentsMargins(0, 0, 0, 50);
|
||||||
|
|
||||||
|
register_button_ = new RaisedButton("REGISTER", this);
|
||||||
|
register_button_->setBackgroundColor(QColor("#171919"));
|
||||||
|
register_button_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
register_button_->setMinimumSize(350, 65);
|
||||||
|
register_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
register_button_->setFontSize(17);
|
||||||
|
register_button_->setCornerRadius(3);
|
||||||
|
|
||||||
|
button_layout_->addStretch(1);
|
||||||
|
button_layout_->addWidget(register_button_);
|
||||||
|
button_layout_->addStretch(1);
|
||||||
|
|
||||||
|
top_layout_->addLayout(back_layout_);
|
||||||
|
top_layout_->addStretch(1);
|
||||||
|
top_layout_->addLayout(logo_layout_);
|
||||||
|
top_layout_->addLayout(form_wrapper_);
|
||||||
|
top_layout_->addStretch(2);
|
||||||
|
top_layout_->addLayout(button_layout_);
|
||||||
|
top_layout_->addStretch(1);
|
||||||
|
|
||||||
|
connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
|
||||||
|
connect(register_button_, SIGNAL(clicked()), this, SLOT(onRegisterButtonClicked()));
|
||||||
|
|
||||||
|
connect(username_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
|
||||||
|
connect(password_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
|
||||||
|
connect(password_confirmation_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
|
||||||
|
connect(server_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
|
||||||
|
|
||||||
|
username_input_->setValidator(validator_->localpart_);
|
||||||
|
password_input_->setValidator(validator_->password_);
|
||||||
|
server_input_->setValidator(validator_->domain_);
|
||||||
|
|
||||||
|
setLayout(top_layout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterPage::onBackButtonClicked()
|
||||||
|
{
|
||||||
|
emit backButtonClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterPage::onRegisterButtonClicked()
|
||||||
|
{
|
||||||
|
if (!username_input_->hasAcceptableInput()) {
|
||||||
|
QString text("Invalid username");
|
||||||
|
QPoint point = username_input_->mapToGlobal(username_input_->rect().topRight());
|
||||||
|
QToolTip::showText(point, text);
|
||||||
|
} else if (!password_input_->hasAcceptableInput()) {
|
||||||
|
QString text("Password is not long enough");
|
||||||
|
QPoint point = password_input_->mapToGlobal(password_input_->rect().topRight());
|
||||||
|
QToolTip::showText(point, text);
|
||||||
|
} else if (password_input_->text() != password_confirmation_->text()) {
|
||||||
|
QString text("Passwords don't match");
|
||||||
|
QPoint point = password_confirmation_->mapToGlobal(password_confirmation_->rect().topRight());
|
||||||
|
QToolTip::showText(point, text);
|
||||||
|
} else if (!server_input_->hasAcceptableInput()) {
|
||||||
|
QString text("Invalid server name");
|
||||||
|
QPoint point = server_input_->mapToGlobal(server_input_->rect().topRight());
|
||||||
|
QToolTip::showText(point, text);
|
||||||
|
} else {
|
||||||
|
QString username = username_input_->text();
|
||||||
|
QString password = password_input_->text();
|
||||||
|
QString server = server_input_->text();
|
||||||
|
|
||||||
|
emit registerUser(username, password, server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterPage::~RegisterPage()
|
||||||
|
{
|
||||||
|
}
|
71
src/RoomInfo.cc
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RoomInfo.h"
|
||||||
|
|
||||||
|
RoomInfo::RoomInfo()
|
||||||
|
: name_("")
|
||||||
|
, topic_("")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomInfo::RoomInfo(QString name, QString topic, QUrl avatar_url)
|
||||||
|
: name_(name)
|
||||||
|
, topic_(topic)
|
||||||
|
, avatar_url_(avatar_url)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RoomInfo::id() const
|
||||||
|
{
|
||||||
|
return id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RoomInfo::name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RoomInfo::topic() const
|
||||||
|
{
|
||||||
|
return topic_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl RoomInfo::avatarUrl() const
|
||||||
|
{
|
||||||
|
return avatar_url_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfo::setAvatarUrl(const QUrl &url)
|
||||||
|
{
|
||||||
|
avatar_url_ = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfo::setId(const QString &id)
|
||||||
|
{
|
||||||
|
id_ = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfo::setName(const QString &name)
|
||||||
|
{
|
||||||
|
name_ = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfo::setTopic(const QString &topic)
|
||||||
|
{
|
||||||
|
topic_ = topic;
|
||||||
|
}
|
138
src/RoomInfoListItem.cc
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "Ripple.h"
|
||||||
|
#include "RoomInfo.h"
|
||||||
|
#include "RoomInfoListItem.h"
|
||||||
|
|
||||||
|
RoomInfoListItem::RoomInfoListItem(RoomInfo info, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, info_(info)
|
||||||
|
, is_pressed_(false)
|
||||||
|
, max_height_(65)
|
||||||
|
{
|
||||||
|
normal_style_ =
|
||||||
|
"QWidget { background-color: #5d6565; color: #ebebeb;"
|
||||||
|
"border-bottom: 1px solid #171919;}"
|
||||||
|
"QLabel { border: none; }";
|
||||||
|
|
||||||
|
pressed_style_ =
|
||||||
|
"QWidget { background-color: #577275; color: #ebebeb;"
|
||||||
|
"border-bottom: 1px solid #171919;}"
|
||||||
|
"QLabel { border: none; }";
|
||||||
|
|
||||||
|
setStyleSheet(normal_style_);
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
|
||||||
|
setMinimumSize(parent->width(), max_height_);
|
||||||
|
|
||||||
|
topLayout_ = new QHBoxLayout(this);
|
||||||
|
topLayout_->setSpacing(0);
|
||||||
|
topLayout_->setMargin(0);
|
||||||
|
|
||||||
|
avatarWidget_ = new QWidget(this);
|
||||||
|
avatarWidget_->setMaximumSize(max_height_, max_height_);
|
||||||
|
textWidget_ = new QWidget(this);
|
||||||
|
|
||||||
|
avatarLayout_ = new QVBoxLayout(avatarWidget_);
|
||||||
|
avatarLayout_->setSpacing(0);
|
||||||
|
avatarLayout_->setContentsMargins(0, 5, 0, 5);
|
||||||
|
|
||||||
|
textLayout_ = new QVBoxLayout(textWidget_);
|
||||||
|
textLayout_->setSpacing(0);
|
||||||
|
textLayout_->setContentsMargins(0, 5, 0, 5);
|
||||||
|
|
||||||
|
roomAvatar_ = new Avatar(avatarWidget_);
|
||||||
|
roomAvatar_->setLetter(QChar(info_.name()[0]));
|
||||||
|
avatarLayout_->addWidget(roomAvatar_);
|
||||||
|
|
||||||
|
roomName_ = new QLabel(textWidget_);
|
||||||
|
roomName_->setText(info_.name());
|
||||||
|
roomName_->setMaximumSize(230, max_height_ / 2);
|
||||||
|
roomName_->setStyleSheet("font-weight: 500; font-size: 11.5pt");
|
||||||
|
roomName_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
roomTopic_ = new QLabel(textWidget_);
|
||||||
|
roomTopic_->setText(info_.topic());
|
||||||
|
roomTopic_->setMaximumSize(230, max_height_ / 2);
|
||||||
|
roomTopic_->setStyleSheet("color: #c9c9c9; font-size: 10pt");
|
||||||
|
roomTopic_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
textLayout_->addWidget(roomName_);
|
||||||
|
textLayout_->addWidget(roomTopic_);
|
||||||
|
|
||||||
|
topLayout_->addWidget(avatarWidget_);
|
||||||
|
topLayout_->addWidget(textWidget_);
|
||||||
|
|
||||||
|
setElidedText(roomName_, info_.name(), 220);
|
||||||
|
setElidedText(roomTopic_, info_.topic(), 220);
|
||||||
|
|
||||||
|
QPainterPath path;
|
||||||
|
path.addRoundedRect(rect(), 0, 0);
|
||||||
|
|
||||||
|
ripple_overlay_ = new RippleOverlay(this);
|
||||||
|
ripple_overlay_->setClipPath(path);
|
||||||
|
ripple_overlay_->setClipping(true);
|
||||||
|
|
||||||
|
setLayout(topLayout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfoListItem::setPressedState(bool state)
|
||||||
|
{
|
||||||
|
if (!is_pressed_ && state) {
|
||||||
|
is_pressed_ = state;
|
||||||
|
setStyleSheet(pressed_style_);
|
||||||
|
} else if (is_pressed_ && !state) {
|
||||||
|
is_pressed_ = state;
|
||||||
|
setStyleSheet(normal_style_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfoListItem::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
emit clicked(info_);
|
||||||
|
|
||||||
|
setPressedState(true);
|
||||||
|
|
||||||
|
// Ripple on mouse position by default.
|
||||||
|
QPoint pos = event->pos();
|
||||||
|
qreal radiusEndValue = static_cast<qreal>(width()) / 2;
|
||||||
|
|
||||||
|
Ripple *ripple = new Ripple(pos);
|
||||||
|
|
||||||
|
ripple->setRadiusEndValue(radiusEndValue);
|
||||||
|
ripple->setOpacityStartValue(0.35);
|
||||||
|
ripple->setColor(QColor("#171919"));
|
||||||
|
ripple->radiusAnimation()->setDuration(300);
|
||||||
|
ripple->opacityAnimation()->setDuration(500);
|
||||||
|
|
||||||
|
ripple_overlay_->addRipple(ripple);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomInfoListItem::setElidedText(QLabel *label, QString text, int width)
|
||||||
|
{
|
||||||
|
QFontMetrics metrics(label->font());
|
||||||
|
QString elidedText = metrics.elidedText(text, Qt::ElideRight, width);
|
||||||
|
label->setText(elidedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomInfoListItem::~RoomInfoListItem()
|
||||||
|
{
|
||||||
|
}
|
119
src/RoomList.cc
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui_RoomList.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "RoomInfoListItem.h"
|
||||||
|
#include "RoomList.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
RoomList::RoomList(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::RoomList)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomList::~RoomList()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomInfo RoomList::extractRoomInfo(const State &room_state)
|
||||||
|
{
|
||||||
|
RoomInfo info;
|
||||||
|
|
||||||
|
auto events = room_state.events();
|
||||||
|
|
||||||
|
for (int i = 0; i < events.count(); i++) {
|
||||||
|
if (events[i].type() == "m.room.name") {
|
||||||
|
info.setName(events[i].content().value("name").toString());
|
||||||
|
} else if (events[i].type() == "m.room.topic") {
|
||||||
|
info.setTopic(events[i].content().value("topic").toString());
|
||||||
|
} else if (events[i].type() == "m.room.avatar") {
|
||||||
|
info.setAvatarUrl(QUrl(events[i].content().value("url").toString()));
|
||||||
|
} else if (events[i].type() == "m.room.canonical_alias") {
|
||||||
|
if (info.name().isEmpty())
|
||||||
|
info.setName(events[i].content().value("alias").toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomList::setInitialRooms(const Rooms &rooms)
|
||||||
|
{
|
||||||
|
available_rooms_.clear();
|
||||||
|
|
||||||
|
for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) {
|
||||||
|
RoomInfo info = RoomList::extractRoomInfo(it.value().state());
|
||||||
|
info.setId(it.key());
|
||||||
|
|
||||||
|
if (info.name().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!info.avatarUrl().isEmpty())
|
||||||
|
emit fetchRoomAvatar(info.id(), info.avatarUrl());
|
||||||
|
|
||||||
|
RoomInfoListItem *room_item = new RoomInfoListItem(info, ui->scrollArea);
|
||||||
|
connect(room_item,
|
||||||
|
SIGNAL(clicked(const RoomInfo &)),
|
||||||
|
this,
|
||||||
|
SLOT(highlightSelectedRoom(const RoomInfo &)));
|
||||||
|
|
||||||
|
available_rooms_.insert(it.key(), room_item);
|
||||||
|
|
||||||
|
ui->scrollVerticalLayout->addWidget(room_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move this into its own function.
|
||||||
|
auto first_room = available_rooms_.first();
|
||||||
|
first_room->setPressedState(true);
|
||||||
|
emit roomChanged(first_room->info());
|
||||||
|
|
||||||
|
ui->scrollVerticalLayout->addStretch(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomList::highlightSelectedRoom(const RoomInfo &info)
|
||||||
|
{
|
||||||
|
emit roomChanged(info);
|
||||||
|
|
||||||
|
for (auto it = available_rooms_.constBegin(); it != available_rooms_.constEnd(); it++) {
|
||||||
|
if (it.key() != info.id())
|
||||||
|
it.value()->setPressedState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomList::updateRoomAvatar(const QString &roomid, const QImage &avatar_image)
|
||||||
|
{
|
||||||
|
if (!available_rooms_.contains(roomid)) {
|
||||||
|
qDebug() << "Avatar update on non existent room" << roomid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto list_item = available_rooms_.value(roomid);
|
||||||
|
list_item->setAvatar(avatar_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoomList::appendRoom(QString name)
|
||||||
|
{
|
||||||
|
Q_UNUSED(name);
|
||||||
|
}
|
151
src/SlidingStackWidget.cc
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SlidingStackWidget.h"
|
||||||
|
|
||||||
|
SlidingStackWidget::SlidingStackWidget(QWidget *parent)
|
||||||
|
: QStackedWidget(parent)
|
||||||
|
{
|
||||||
|
window_ = parent;
|
||||||
|
|
||||||
|
if (parent == Q_NULLPTR) {
|
||||||
|
qDebug() << "Using nullptr for parent";
|
||||||
|
window_ = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_position_ = QPoint(0, 0);
|
||||||
|
speed_ = 400;
|
||||||
|
now_ = 0;
|
||||||
|
next_ = 0;
|
||||||
|
active_ = false;
|
||||||
|
animation_type_ = QEasingCurve::InOutCirc;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlidingStackWidget::~SlidingStackWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlidingStackWidget::slideInNext()
|
||||||
|
{
|
||||||
|
int now = currentIndex();
|
||||||
|
|
||||||
|
if (now < count() - 1)
|
||||||
|
slideInIndex(now + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlidingStackWidget::slideInPrevious()
|
||||||
|
{
|
||||||
|
int now = currentIndex();
|
||||||
|
|
||||||
|
if (now > 0)
|
||||||
|
slideInIndex(now - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlidingStackWidget::slideInIndex(int index, AnimationDirection direction)
|
||||||
|
{
|
||||||
|
// Take into consideration possible index overflow/undeflow.
|
||||||
|
if (index > count() - 1) {
|
||||||
|
direction = AnimationDirection::RIGHT_TO_LEFT;
|
||||||
|
index = index % count();
|
||||||
|
} else if (index < 0) {
|
||||||
|
direction = AnimationDirection::LEFT_TO_RIGHT;
|
||||||
|
index = (index + count()) % count();
|
||||||
|
}
|
||||||
|
|
||||||
|
slideInWidget(widget(index), direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlidingStackWidget::slideInWidget(QWidget *next_widget, AnimationDirection direction)
|
||||||
|
{
|
||||||
|
// If an animation is currenlty executing we should wait for it to finish before
|
||||||
|
// another transition can start.
|
||||||
|
if (active_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
active_ = true;
|
||||||
|
|
||||||
|
int now = currentIndex();
|
||||||
|
int next = indexOf(next_widget);
|
||||||
|
|
||||||
|
if (now == next) {
|
||||||
|
active_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset_x = frameRect().width();
|
||||||
|
|
||||||
|
next_widget->setGeometry(0, 0, offset_x, 0);
|
||||||
|
|
||||||
|
if (direction == AnimationDirection::LEFT_TO_RIGHT) {
|
||||||
|
offset_x = -offset_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint pnext = next_widget->pos();
|
||||||
|
QPoint pnow = widget(now)->pos();
|
||||||
|
current_position_ = pnow;
|
||||||
|
|
||||||
|
// Reposition the next widget outside of the display area.
|
||||||
|
next_widget->move(pnext.x() - offset_x, pnext.y());
|
||||||
|
|
||||||
|
// Make the widget visible.
|
||||||
|
next_widget->show();
|
||||||
|
next_widget->raise();
|
||||||
|
|
||||||
|
// Animate both the next and now widget.
|
||||||
|
QPropertyAnimation *animation_now = new QPropertyAnimation(widget(now), "pos");
|
||||||
|
|
||||||
|
animation_now->setDuration(speed_);
|
||||||
|
animation_now->setEasingCurve(animation_type_);
|
||||||
|
animation_now->setStartValue(QPoint(pnow.x(), pnow.y()));
|
||||||
|
animation_now->setEndValue(QPoint(pnow.x() + offset_x, pnow.y()));
|
||||||
|
|
||||||
|
QPropertyAnimation *animation_next = new QPropertyAnimation(next_widget, "pos");
|
||||||
|
|
||||||
|
animation_next->setDuration(speed_);
|
||||||
|
animation_next->setEasingCurve(animation_type_);
|
||||||
|
animation_next->setStartValue(QPoint(pnext.x() - offset_x, pnext.y()));
|
||||||
|
animation_next->setEndValue(QPoint(pnext.x(), pnext.y()));
|
||||||
|
|
||||||
|
QParallelAnimationGroup *animation_group = new QParallelAnimationGroup;
|
||||||
|
|
||||||
|
animation_group->addAnimation(animation_now);
|
||||||
|
animation_group->addAnimation(animation_next);
|
||||||
|
|
||||||
|
connect(animation_group, SIGNAL(finished()), this, SLOT(onAnimationFinished()));
|
||||||
|
|
||||||
|
next_ = next;
|
||||||
|
now_ = now;
|
||||||
|
animation_group->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlidingStackWidget::onAnimationFinished()
|
||||||
|
{
|
||||||
|
setCurrentIndex(next_);
|
||||||
|
|
||||||
|
// The old widget is no longer necessary so we can hide it and
|
||||||
|
// move it back to its original position.
|
||||||
|
widget(now_)->hide();
|
||||||
|
widget(now_)->move(current_position_);
|
||||||
|
|
||||||
|
active_ = false;
|
||||||
|
emit animationFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SlidingStackWidget::getWidgetIndex(QWidget *widget)
|
||||||
|
{
|
||||||
|
return indexOf(widget);
|
||||||
|
}
|
290
src/Sync.cc
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
#include "Deserializable.h"
|
||||||
|
#include "Sync.h"
|
||||||
|
|
||||||
|
QString SyncResponse::nextBatch() const
|
||||||
|
{
|
||||||
|
return next_batch_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyncResponse::deserialize(QJsonDocument data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("Sync response is not a JSON object");
|
||||||
|
|
||||||
|
QJsonObject object = data.object();
|
||||||
|
|
||||||
|
if (object.value("next_batch") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Sync: missing next_batch parameter");
|
||||||
|
|
||||||
|
if (object.value("rooms") == QJsonValue::Undefined)
|
||||||
|
throw DeserializationException("Sync: missing rooms parameter");
|
||||||
|
|
||||||
|
rooms_.deserialize(object.value("rooms"));
|
||||||
|
next_batch_ = object.value("next_batch").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rooms SyncResponse::rooms() const
|
||||||
|
{
|
||||||
|
return rooms_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, JoinedRoom> Rooms::join() const
|
||||||
|
{
|
||||||
|
return join_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rooms::deserialize(QJsonValue data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("Rooms value is not a JSON object");
|
||||||
|
|
||||||
|
QJsonObject object = data.toObject();
|
||||||
|
|
||||||
|
if (!object.contains("join"))
|
||||||
|
throw DeserializationException("rooms/join is missing");
|
||||||
|
|
||||||
|
if (!object.contains("invite"))
|
||||||
|
throw DeserializationException("rooms/invite is missing");
|
||||||
|
|
||||||
|
if (!object.contains("leave"))
|
||||||
|
throw DeserializationException("rooms/leave is missing");
|
||||||
|
|
||||||
|
if (!object.value("join").isObject())
|
||||||
|
throw DeserializationException("rooms/join must be a JSON object");
|
||||||
|
|
||||||
|
if (!object.value("invite").isObject())
|
||||||
|
throw DeserializationException("rooms/invite must be a JSON object");
|
||||||
|
|
||||||
|
if (!object.value("leave").isObject())
|
||||||
|
throw DeserializationException("rooms/leave must be a JSON object");
|
||||||
|
|
||||||
|
auto join = object.value("join").toObject();
|
||||||
|
|
||||||
|
for (auto it = join.constBegin(); it != join.constEnd(); it++) {
|
||||||
|
JoinedRoom tmp_room;
|
||||||
|
|
||||||
|
try {
|
||||||
|
tmp_room.deserialize(it.value());
|
||||||
|
join_.insert(it.key(), tmp_room);
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << e.what();
|
||||||
|
qWarning() << "Skipping malformed object for room" << it.key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
State JoinedRoom::state() const
|
||||||
|
{
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timeline JoinedRoom::timeline() const
|
||||||
|
{
|
||||||
|
return timeline_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoinedRoom::deserialize(QJsonValue data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("JoinedRoom is not a JSON object");
|
||||||
|
|
||||||
|
QJsonObject object = data.toObject();
|
||||||
|
|
||||||
|
if (!object.contains("state"))
|
||||||
|
throw DeserializationException("join/state is missing");
|
||||||
|
|
||||||
|
if (!object.contains("timeline"))
|
||||||
|
throw DeserializationException("join/timeline is missing");
|
||||||
|
|
||||||
|
if (!object.contains("account_data"))
|
||||||
|
throw DeserializationException("join/account_data is missing");
|
||||||
|
|
||||||
|
if (!object.contains("unread_notifications"))
|
||||||
|
throw DeserializationException("join/unread_notifications is missing");
|
||||||
|
|
||||||
|
if (!object.value("state").isObject())
|
||||||
|
throw DeserializationException("join/state should be an object");
|
||||||
|
|
||||||
|
QJsonObject state = object.value("state").toObject();
|
||||||
|
|
||||||
|
if (!state.contains("events"))
|
||||||
|
throw DeserializationException("join/state/events is missing");
|
||||||
|
|
||||||
|
state_.deserialize(state.value("events"));
|
||||||
|
timeline_.deserialize(object.value("timeline"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject Event::content() const
|
||||||
|
{
|
||||||
|
return content_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject Event::unsigned_content() const
|
||||||
|
{
|
||||||
|
return unsigned_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Event::sender() const
|
||||||
|
{
|
||||||
|
return sender_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Event::state_key() const
|
||||||
|
{
|
||||||
|
return state_key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Event::type() const
|
||||||
|
{
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Event::eventId() const
|
||||||
|
{
|
||||||
|
return event_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Event::timestamp() const
|
||||||
|
{
|
||||||
|
return origin_server_ts_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event::deserialize(QJsonValue data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("Event is not a JSON object");
|
||||||
|
|
||||||
|
QJsonObject object = data.toObject();
|
||||||
|
|
||||||
|
if (!object.contains("content"))
|
||||||
|
throw DeserializationException("event/content is missing");
|
||||||
|
|
||||||
|
if (!object.contains("unsigned"))
|
||||||
|
throw DeserializationException("event/content is missing");
|
||||||
|
|
||||||
|
if (!object.contains("sender"))
|
||||||
|
throw DeserializationException("event/sender is missing");
|
||||||
|
|
||||||
|
if (!object.contains("event_id"))
|
||||||
|
throw DeserializationException("event/event_id is missing");
|
||||||
|
|
||||||
|
// TODO: Make this optional
|
||||||
|
/* if (!object.contains("state_key")) */
|
||||||
|
/* throw DeserializationException("event/state_key is missing"); */
|
||||||
|
|
||||||
|
if (!object.contains("type"))
|
||||||
|
throw DeserializationException("event/type is missing");
|
||||||
|
|
||||||
|
if (!object.contains("origin_server_ts"))
|
||||||
|
throw DeserializationException("event/origin_server_ts is missing");
|
||||||
|
|
||||||
|
content_ = object.value("content").toObject();
|
||||||
|
unsigned_ = object.value("unsigned").toObject();
|
||||||
|
|
||||||
|
sender_ = object.value("sender").toString();
|
||||||
|
state_key_ = object.value("state_key").toString();
|
||||||
|
type_ = object.value("type").toString();
|
||||||
|
event_id_ = object.value("event_id").toString();
|
||||||
|
|
||||||
|
origin_server_ts_ = object.value("origin_server_ts").toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Event> State::events() const
|
||||||
|
{
|
||||||
|
return events_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::deserialize(QJsonValue data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isArray())
|
||||||
|
throw DeserializationException("State is not a JSON array");
|
||||||
|
|
||||||
|
QJsonArray event_array = data.toArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < event_array.count(); i++) {
|
||||||
|
Event event;
|
||||||
|
|
||||||
|
try {
|
||||||
|
event.deserialize(event_array.at(i));
|
||||||
|
events_.push_back(event);
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << e.what();
|
||||||
|
qWarning() << "Skipping malformed state event";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Event> Timeline::events() const
|
||||||
|
{
|
||||||
|
return events_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Timeline::previousBatch() const
|
||||||
|
{
|
||||||
|
return prev_batch_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timeline::limited() const
|
||||||
|
{
|
||||||
|
return limited_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timeline::deserialize(QJsonValue data) throw(DeserializationException)
|
||||||
|
{
|
||||||
|
if (!data.isObject())
|
||||||
|
throw DeserializationException("Timeline is not a JSON object");
|
||||||
|
|
||||||
|
auto object = data.toObject();
|
||||||
|
|
||||||
|
if (!object.contains("events"))
|
||||||
|
throw DeserializationException("timeline/events is missing");
|
||||||
|
|
||||||
|
if (!object.contains("prev_batch"))
|
||||||
|
throw DeserializationException("timeline/prev_batch is missing");
|
||||||
|
|
||||||
|
if (!object.contains("limited"))
|
||||||
|
throw DeserializationException("timeline/limited is missing");
|
||||||
|
|
||||||
|
prev_batch_ = object.value("prev_batch").toString();
|
||||||
|
limited_ = object.value("limited").toBool();
|
||||||
|
|
||||||
|
if (!object.value("events").isArray())
|
||||||
|
throw DeserializationException("timeline/events is not a JSON array");
|
||||||
|
|
||||||
|
auto timeline_events = object.value("events").toArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < timeline_events.count(); i++) {
|
||||||
|
Event event;
|
||||||
|
|
||||||
|
try {
|
||||||
|
event.deserialize(timeline_events.at(i));
|
||||||
|
events_.push_back(event);
|
||||||
|
} catch (DeserializationException &e) {
|
||||||
|
qWarning() << e.what();
|
||||||
|
qWarning() << "Skipping malformed timeline event";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
src/TextInputWidget.cc
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
#include "TextInputWidget.h"
|
||||||
|
|
||||||
|
TextInputWidget::TextInputWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
setCursor(Qt::ArrowCursor);
|
||||||
|
setStyleSheet("background-color: #171919; height: 45px;");
|
||||||
|
|
||||||
|
top_layout_ = new QHBoxLayout();
|
||||||
|
top_layout_->setSpacing(6);
|
||||||
|
top_layout_->setContentsMargins(6, 0, 0, 0);
|
||||||
|
|
||||||
|
send_file_button_ = new FlatButton(this);
|
||||||
|
send_file_button_->setCursor(Qt::PointingHandCursor);
|
||||||
|
|
||||||
|
QIcon send_file_icon;
|
||||||
|
send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
send_file_button_->setForegroundColor(QColor("#577275"));
|
||||||
|
send_file_button_->setIcon(send_file_icon);
|
||||||
|
send_file_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
|
input_ = new QLineEdit(this);
|
||||||
|
input_->setPlaceholderText("Write a message...");
|
||||||
|
input_->setStyleSheet("color: #ebebeb; font-size: 10pt; border-radius: 0; padding: 2px; margin-bottom: 4px;");
|
||||||
|
|
||||||
|
send_message_button_ = new FlatButton(this);
|
||||||
|
send_message_button_->setCursor(Qt::PointingHandCursor);
|
||||||
|
send_message_button_->setForegroundColor(QColor("#577275"));
|
||||||
|
|
||||||
|
QIcon send_message_icon;
|
||||||
|
send_message_icon.addFile(":/icons/icons/share-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
send_message_button_->setIcon(send_message_icon);
|
||||||
|
send_message_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
|
top_layout_->addWidget(send_file_button_);
|
||||||
|
top_layout_->addWidget(input_);
|
||||||
|
top_layout_->addWidget(send_message_button_);
|
||||||
|
|
||||||
|
setLayout(top_layout_);
|
||||||
|
|
||||||
|
connect(send_message_button_, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
|
||||||
|
connect(input_, SIGNAL(returnPressed()), send_message_button_, SIGNAL(clicked()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextInputWidget::onSendButtonClicked()
|
||||||
|
{
|
||||||
|
auto msg_text = input_->text();
|
||||||
|
|
||||||
|
if (msg_text.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit sendTextMessage(msg_text);
|
||||||
|
input_->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextInputWidget::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
|
QStyleOption option;
|
||||||
|
option.initFrom(this);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextInputWidget::~TextInputWidget()
|
||||||
|
{
|
||||||
|
}
|
93
src/TopRoomBar.cc
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
#include "TopRoomBar.h"
|
||||||
|
|
||||||
|
TopRoomBar::TopRoomBar(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
setMinimumSize(QSize(0, 70));
|
||||||
|
setStyleSheet("background-color: #171919; color: #ebebeb;");
|
||||||
|
|
||||||
|
top_layout_ = new QHBoxLayout();
|
||||||
|
top_layout_->setSpacing(10);
|
||||||
|
top_layout_->setContentsMargins(10, 10, 0, 10);
|
||||||
|
|
||||||
|
avatar_ = new Avatar(this);
|
||||||
|
avatar_->setLetter(QChar('?'));
|
||||||
|
avatar_->setBackgroundColor(QColor("#ebebeb"));
|
||||||
|
avatar_->setSize(45);
|
||||||
|
|
||||||
|
text_layout_ = new QVBoxLayout();
|
||||||
|
text_layout_->setSpacing(0);
|
||||||
|
text_layout_->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
name_label_ = new QLabel(this);
|
||||||
|
name_label_->setStyleSheet("font-size: 11pt;");
|
||||||
|
|
||||||
|
topic_label_ = new QLabel(this);
|
||||||
|
topic_label_->setStyleSheet("font-size: 10pt; color: #6c7278;");
|
||||||
|
|
||||||
|
text_layout_->addWidget(name_label_);
|
||||||
|
text_layout_->addWidget(topic_label_);
|
||||||
|
|
||||||
|
settings_button_ = new FlatButton(this);
|
||||||
|
settings_button_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
settings_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
settings_button_->setStyleSheet("width: 30px; height: 30px;");
|
||||||
|
|
||||||
|
QIcon settings_icon;
|
||||||
|
settings_icon.addFile(":/icons/icons/cog.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
settings_button_->setIcon(settings_icon);
|
||||||
|
settings_button_->setIconSize(QSize(16, 16));
|
||||||
|
|
||||||
|
search_button_ = new FlatButton(this);
|
||||||
|
search_button_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
search_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
search_button_->setStyleSheet("width: 30px; height: 30px;");
|
||||||
|
|
||||||
|
QIcon search_icon;
|
||||||
|
search_icon.addFile(":/icons/icons/search.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
search_button_->setIcon(search_icon);
|
||||||
|
search_button_->setIconSize(QSize(16, 16));
|
||||||
|
|
||||||
|
top_layout_->addWidget(avatar_);
|
||||||
|
top_layout_->addLayout(text_layout_);
|
||||||
|
top_layout_->addStretch(1);
|
||||||
|
top_layout_->addWidget(search_button_);
|
||||||
|
top_layout_->addWidget(settings_button_);
|
||||||
|
|
||||||
|
setLayout(top_layout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopRoomBar::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
|
||||||
|
QStyleOption option;
|
||||||
|
option.initFrom(this);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TopRoomBar::~TopRoomBar()
|
||||||
|
{
|
||||||
|
}
|
104
src/UserInfoWidget.cc
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UserInfoWidget.h"
|
||||||
|
#include "FlatButton.h"
|
||||||
|
|
||||||
|
UserInfoWidget::UserInfoWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, display_name_("User")
|
||||||
|
, userid_("@user:homeserver.org")
|
||||||
|
{
|
||||||
|
QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
setSizePolicy(sizePolicy);
|
||||||
|
setMinimumSize(QSize(0, 65));
|
||||||
|
|
||||||
|
topLayout_ = new QHBoxLayout(this);
|
||||||
|
topLayout_->setSpacing(0);
|
||||||
|
topLayout_->setContentsMargins(5, 5, 5, 5);
|
||||||
|
|
||||||
|
avatarLayout_ = new QHBoxLayout();
|
||||||
|
textLayout_ = new QVBoxLayout();
|
||||||
|
|
||||||
|
userAvatar_ = new Avatar(this);
|
||||||
|
userAvatar_->setLetter(QChar('?'));
|
||||||
|
userAvatar_->setSize(50);
|
||||||
|
userAvatar_->setMaximumSize(QSize(55, 55));
|
||||||
|
|
||||||
|
displayNameLabel_ = new QLabel(this);
|
||||||
|
displayNameLabel_->setStyleSheet(
|
||||||
|
"padding: 0 9px;"
|
||||||
|
"color: #ebebeb;"
|
||||||
|
"font-size: 11pt;"
|
||||||
|
"margin-bottom: -10px;");
|
||||||
|
displayNameLabel_->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
|
||||||
|
|
||||||
|
userIdLabel_ = new QLabel(this);
|
||||||
|
userIdLabel_->setStyleSheet(
|
||||||
|
"padding: 0 8px 8px 8px;"
|
||||||
|
"color: #5D6565;"
|
||||||
|
"font-size: 10pt;");
|
||||||
|
userIdLabel_->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
|
||||||
|
avatarLayout_->addWidget(userAvatar_);
|
||||||
|
textLayout_->addWidget(displayNameLabel_);
|
||||||
|
textLayout_->addWidget(userIdLabel_);
|
||||||
|
|
||||||
|
topLayout_->addLayout(avatarLayout_);
|
||||||
|
topLayout_->addLayout(textLayout_);
|
||||||
|
topLayout_->addStretch(1);
|
||||||
|
|
||||||
|
buttonLayout_ = new QHBoxLayout();
|
||||||
|
|
||||||
|
settingsButton_ = new FlatButton(this);
|
||||||
|
settingsButton_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
settingsButton_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
settingsButton_->setStyleSheet("width: 30px; height: 30px;");
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":/icons/icons/user-shape.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
|
|
||||||
|
settingsButton_->setIcon(icon);
|
||||||
|
settingsButton_->setIconSize(QSize(16, 16));
|
||||||
|
|
||||||
|
buttonLayout_->addWidget(settingsButton_);
|
||||||
|
|
||||||
|
topLayout_->addLayout(buttonLayout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInfoWidget::~UserInfoWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfoWidget::setAvatar(const QImage &img)
|
||||||
|
{
|
||||||
|
avatar_image_ = img;
|
||||||
|
userAvatar_->setImage(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfoWidget::setDisplayName(const QString &name)
|
||||||
|
{
|
||||||
|
display_name_ = name;
|
||||||
|
displayNameLabel_->setText(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserInfoWidget::setUserId(const QString &userid)
|
||||||
|
{
|
||||||
|
userid_ = userid;
|
||||||
|
userIdLabel_->setText(userid);
|
||||||
|
}
|
103
src/WelcomePage.cc
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QLayout>
|
||||||
|
|
||||||
|
#include "WelcomePage.h"
|
||||||
|
|
||||||
|
WelcomePage::WelcomePage(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
top_layout_ = new QVBoxLayout(this);
|
||||||
|
top_layout_->setSpacing(0);
|
||||||
|
top_layout_->setMargin(0);
|
||||||
|
|
||||||
|
intro_banner_ = new QLabel(this);
|
||||||
|
intro_banner_->setStyleSheet("background-color: #1c3133;");
|
||||||
|
intro_banner_->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
intro_text_ = new QLabel(this);
|
||||||
|
intro_text_->setText(QApplication::translate("WelcomePage",
|
||||||
|
"<html>"
|
||||||
|
"<head/>"
|
||||||
|
"<body>"
|
||||||
|
" <p align=\"center\"><span style=\" font-size:28pt;\"> nheko </span></p>"
|
||||||
|
" <p align=\"center\" style=\"margin: 0; line-height: 2pt\">"
|
||||||
|
" <span style=\" font-size:12pt; color:#6d7387;\"> "
|
||||||
|
" A desktop client for Matrix, the open protocol for decentralized communication."
|
||||||
|
" </span>"
|
||||||
|
" </p>\n"
|
||||||
|
" <p align=\"center\" style=\"margin: 1pt; line-height: 2pt;\">"
|
||||||
|
" <span style=\" font-size:12pt; color:#6d7387;\">Enjoy your stay!</span>"
|
||||||
|
" </p>"
|
||||||
|
"</body>"
|
||||||
|
"</html>",
|
||||||
|
Q_NULLPTR));
|
||||||
|
|
||||||
|
top_layout_->addWidget(intro_banner_);
|
||||||
|
top_layout_->addWidget(intro_text_, 0, Qt::AlignCenter);
|
||||||
|
|
||||||
|
button_layout_ = new QHBoxLayout();
|
||||||
|
button_layout_->setSpacing(0);
|
||||||
|
button_layout_->setContentsMargins(0, 20, 0, 80);
|
||||||
|
|
||||||
|
register_button_ = new RaisedButton("REGISTER", this);
|
||||||
|
register_button_->setBackgroundColor(QColor("#171919"));
|
||||||
|
register_button_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
register_button_->setMinimumSize(240, 60);
|
||||||
|
register_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
register_button_->setFontSize(14);
|
||||||
|
register_button_->setCornerRadius(3);
|
||||||
|
|
||||||
|
login_button_ = new RaisedButton("LOGIN", this);
|
||||||
|
login_button_->setBackgroundColor(QColor("#171919"));
|
||||||
|
login_button_->setForegroundColor(QColor("#ebebeb"));
|
||||||
|
login_button_->setMinimumSize(240, 60);
|
||||||
|
login_button_->setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
|
login_button_->setFontSize(14);
|
||||||
|
login_button_->setCornerRadius(3);
|
||||||
|
|
||||||
|
button_spacer_ = new QSpacerItem(20, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
button_layout_->addStretch(1);
|
||||||
|
button_layout_->addWidget(register_button_);
|
||||||
|
button_layout_->addItem(button_spacer_);
|
||||||
|
button_layout_->addWidget(login_button_);
|
||||||
|
button_layout_->addStretch(1);
|
||||||
|
|
||||||
|
top_layout_->addLayout(button_layout_);
|
||||||
|
|
||||||
|
connect(register_button_, SIGNAL(clicked()), this, SLOT(onRegisterButtonClicked()));
|
||||||
|
connect(login_button_, SIGNAL(clicked()), this, SLOT(onLoginButtonClicked()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WelcomePage::onLoginButtonClicked()
|
||||||
|
{
|
||||||
|
emit userLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WelcomePage::onRegisterButtonClicked()
|
||||||
|
{
|
||||||
|
emit userRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
WelcomePage::~WelcomePage()
|
||||||
|
{
|
||||||
|
}
|
48
src/main.cc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication::setApplicationName("nheko");
|
||||||
|
QCoreApplication::setApplicationVersion("Ωμέγa");
|
||||||
|
QCoreApplication::setOrganizationName("Nheko");
|
||||||
|
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-Light.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-Regular.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-Italic.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-Bold.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-BoldItalic.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-Semibold.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-SemiboldItalic.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-ExtraBold.ttf");
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/OpenSans-ExtraBoldItalic.ttf");
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
QFont font("Open Sans");
|
||||||
|
app.setFont(font);
|
||||||
|
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
143
src/ui/Avatar.cc
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
|
||||||
|
Avatar::Avatar(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
size_ = ui::AvatarSize;
|
||||||
|
type_ = ui::AvatarType::Letter;
|
||||||
|
letter_ = QChar('A');
|
||||||
|
|
||||||
|
QFont _font(font());
|
||||||
|
_font.setPointSizeF(ui::FontSize);
|
||||||
|
setFont(_font);
|
||||||
|
|
||||||
|
QSizePolicy policy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||||
|
setSizePolicy(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Avatar::~Avatar()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Avatar::textColor() const
|
||||||
|
{
|
||||||
|
if (!text_color_.isValid())
|
||||||
|
return QColor("black");
|
||||||
|
|
||||||
|
return text_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Avatar::backgroundColor() const
|
||||||
|
{
|
||||||
|
if (!text_color_.isValid())
|
||||||
|
return QColor("white");
|
||||||
|
|
||||||
|
return background_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Avatar::size() const
|
||||||
|
{
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize Avatar::sizeHint() const
|
||||||
|
{
|
||||||
|
return QSize(size_ + 2, size_ + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::setTextColor(const QColor &color)
|
||||||
|
{
|
||||||
|
text_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::setBackgroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
background_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::setSize(int size)
|
||||||
|
{
|
||||||
|
size_ = size;
|
||||||
|
|
||||||
|
if (!image_.isNull()) {
|
||||||
|
pixmap_ = QPixmap::fromImage(
|
||||||
|
image_.scaled(size_, size_, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
|
}
|
||||||
|
|
||||||
|
QFont _font(font());
|
||||||
|
_font.setPointSizeF(size_ * (ui::FontSize) / 40);
|
||||||
|
|
||||||
|
setFont(_font);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::setLetter(const QChar &letter)
|
||||||
|
{
|
||||||
|
letter_ = letter;
|
||||||
|
type_ = ui::AvatarType::Letter;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::setImage(const QImage &image)
|
||||||
|
{
|
||||||
|
image_ = image;
|
||||||
|
type_ = ui::AvatarType::Image;
|
||||||
|
pixmap_ = QPixmap::fromImage(
|
||||||
|
image_.scaled(size_, size_, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::setIcon(const QIcon &icon)
|
||||||
|
{
|
||||||
|
icon_ = icon;
|
||||||
|
type_ = ui::AvatarType::Icon;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
QRect r = rect();
|
||||||
|
const int hs = size_ / 2;
|
||||||
|
|
||||||
|
if (type_ != ui::AvatarType::Image) {
|
||||||
|
QBrush brush;
|
||||||
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
brush.setColor(backgroundColor());
|
||||||
|
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(brush);
|
||||||
|
painter.drawEllipse(r.center(), hs, hs);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type_) {
|
||||||
|
case ui::AvatarType::Icon: {
|
||||||
|
icon_.paint(&painter,
|
||||||
|
QRect((width() - hs) / 2, (height() - hs) / 2, hs, hs),
|
||||||
|
Qt::AlignCenter,
|
||||||
|
QIcon::Normal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ui::AvatarType::Image: {
|
||||||
|
QPainterPath ppath;
|
||||||
|
ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_);
|
||||||
|
painter.setClipPath(ppath);
|
||||||
|
painter.drawPixmap(QRect(width() / 2 - hs, height() / 2 - hs, size_, size_), pixmap_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ui::AvatarType::Letter: {
|
||||||
|
painter.setPen(textColor());
|
||||||
|
painter.setBrush(Qt::NoBrush);
|
||||||
|
painter.drawText(r.translated(0, -1), Qt::AlignCenter, letter_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
186
src/ui/Badge.cc
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "Badge.h"
|
||||||
|
|
||||||
|
Badge::Badge(QWidget *parent)
|
||||||
|
: OverlayWidget(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Badge::Badge(const QIcon &icon, QWidget *parent)
|
||||||
|
: OverlayWidget(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
Badge::Badge(const QString &text, QWidget *parent)
|
||||||
|
: OverlayWidget(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Badge::~Badge()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::init()
|
||||||
|
{
|
||||||
|
x_ = 0;
|
||||||
|
y_ = 0;
|
||||||
|
padding_ = 10;
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
QFont _font(font());
|
||||||
|
_font.setPointSizeF(10);
|
||||||
|
_font.setStyleName("Bold");
|
||||||
|
|
||||||
|
setFont(_font);
|
||||||
|
setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Badge::text() const
|
||||||
|
{
|
||||||
|
return text_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Badge::icon() const
|
||||||
|
{
|
||||||
|
return icon_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize Badge::sizeHint() const
|
||||||
|
{
|
||||||
|
const int d = getDiameter();
|
||||||
|
return QSize(d + 4, d + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal Badge::relativeYPosition() const
|
||||||
|
{
|
||||||
|
return y_;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal Badge::relativeXPosition() const
|
||||||
|
{
|
||||||
|
return x_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF Badge::relativePosition() const
|
||||||
|
{
|
||||||
|
return QPointF(x_, y_);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Badge::backgroundColor() const
|
||||||
|
{
|
||||||
|
if (!background_color_.isValid())
|
||||||
|
return QColor("black");
|
||||||
|
|
||||||
|
return background_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Badge::textColor() const
|
||||||
|
{
|
||||||
|
if (!text_color_.isValid())
|
||||||
|
return QColor("white");
|
||||||
|
|
||||||
|
return text_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setTextColor(const QColor &color)
|
||||||
|
{
|
||||||
|
text_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setBackgroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
background_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setRelativePosition(const QPointF &pos)
|
||||||
|
{
|
||||||
|
setRelativePosition(pos.x(), pos.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setRelativePosition(qreal x, qreal y)
|
||||||
|
{
|
||||||
|
x_ = x;
|
||||||
|
y_ = y;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setRelativeXPosition(qreal x)
|
||||||
|
{
|
||||||
|
x_ = x;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setRelativeYPosition(qreal y)
|
||||||
|
{
|
||||||
|
y_ = y;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setIcon(const QIcon &icon)
|
||||||
|
{
|
||||||
|
icon_ = icon;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::setText(const QString &text)
|
||||||
|
{
|
||||||
|
text_ = text;
|
||||||
|
|
||||||
|
if (!icon_.isNull())
|
||||||
|
icon_ = QIcon();
|
||||||
|
|
||||||
|
size_ = fontMetrics().size(Qt::TextShowMnemonic, text);
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Badge::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter.translate(x_, y_);
|
||||||
|
|
||||||
|
QBrush brush;
|
||||||
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
brush.setColor(isEnabled() ? backgroundColor() : QColor("#cccccc"));
|
||||||
|
|
||||||
|
painter.setBrush(brush);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
const int d = getDiameter();
|
||||||
|
|
||||||
|
QRectF r(0, 0, d, d);
|
||||||
|
r.translate(QPointF((width() - d), (height() - d)) / 2);
|
||||||
|
|
||||||
|
if (icon_.isNull()) {
|
||||||
|
painter.drawEllipse(r);
|
||||||
|
painter.setPen(textColor());
|
||||||
|
painter.setBrush(Qt::NoBrush);
|
||||||
|
painter.drawText(r.translated(0, -0.5), Qt::AlignCenter, text_);
|
||||||
|
} else {
|
||||||
|
painter.drawEllipse(r);
|
||||||
|
QRectF q(0, 0, 16, 16);
|
||||||
|
q.moveCenter(r.center());
|
||||||
|
QPixmap pixmap = icon().pixmap(16, 16);
|
||||||
|
QPainter icon(&pixmap);
|
||||||
|
icon.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
|
icon.fillRect(pixmap.rect(), textColor());
|
||||||
|
painter.drawPixmap(q.toRect(), pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Badge::getDiameter() const
|
||||||
|
{
|
||||||
|
if (icon_.isNull()) {
|
||||||
|
return qMax(size_.width(), size_.height()) + padding_;
|
||||||
|
}
|
||||||
|
// FIXME: Move this to Theme.h as the default
|
||||||
|
return 24;
|
||||||
|
}
|
761
src/ui/FlatButton.cc
Normal file
|
@ -0,0 +1,761 @@
|
||||||
|
|
||||||
|
#include <QBitmap>
|
||||||
|
#include <QEventTransition>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QSequentialAnimationGroup>
|
||||||
|
#include <QSignalTransition>
|
||||||
|
|
||||||
|
#include "FlatButton.h"
|
||||||
|
#include "Ripple.h"
|
||||||
|
#include "RippleOverlay.h"
|
||||||
|
#include "ThemeManager.h"
|
||||||
|
|
||||||
|
void FlatButton::init()
|
||||||
|
{
|
||||||
|
ripple_overlay_ = new RippleOverlay(this);
|
||||||
|
state_machine_ = new FlatButtonStateMachine(this);
|
||||||
|
role_ = ui::Default;
|
||||||
|
ripple_style_ = ui::PositionedRipple;
|
||||||
|
icon_placement_ = ui::LeftIcon;
|
||||||
|
overlay_style_ = ui::GrayOverlay;
|
||||||
|
bg_mode_ = Qt::TransparentMode;
|
||||||
|
fixed_ripple_radius_ = 64;
|
||||||
|
corner_radius_ = 3;
|
||||||
|
base_opacity_ = 0.13;
|
||||||
|
font_size_ = 10; // 10.5;
|
||||||
|
use_fixed_ripple_radius_ = false;
|
||||||
|
halo_visible_ = false;
|
||||||
|
|
||||||
|
setStyle(&ThemeManager::instance());
|
||||||
|
setAttribute(Qt::WA_Hover);
|
||||||
|
setMouseTracking(true);
|
||||||
|
|
||||||
|
QPainterPath path;
|
||||||
|
path.addRoundedRect(rect(), corner_radius_, corner_radius_);
|
||||||
|
|
||||||
|
ripple_overlay_->setClipPath(path);
|
||||||
|
ripple_overlay_->setClipping(true);
|
||||||
|
|
||||||
|
state_machine_->setupProperties();
|
||||||
|
state_machine_->startAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatButton::FlatButton(QWidget *parent, ui::ButtonPreset preset)
|
||||||
|
: QPushButton(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
applyPreset(preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatButton::FlatButton(const QString &text, QWidget *parent, ui::ButtonPreset preset)
|
||||||
|
: QPushButton(text, parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
applyPreset(preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatButton::FlatButton(const QString &text, ui::Role role, QWidget *parent, ui::ButtonPreset preset)
|
||||||
|
: QPushButton(text, parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
applyPreset(preset);
|
||||||
|
setRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatButton::~FlatButton()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::applyPreset(ui::ButtonPreset preset)
|
||||||
|
{
|
||||||
|
switch (preset) {
|
||||||
|
case ui::FlatPreset:
|
||||||
|
setOverlayStyle(ui::NoOverlay);
|
||||||
|
break;
|
||||||
|
case ui::CheckablePreset:
|
||||||
|
setOverlayStyle(ui::NoOverlay);
|
||||||
|
setCheckable(true);
|
||||||
|
setHaloVisible(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setRole(ui::Role role)
|
||||||
|
{
|
||||||
|
role_ = role;
|
||||||
|
state_machine_->setupProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::Role FlatButton::role() const
|
||||||
|
{
|
||||||
|
return role_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setForegroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
foreground_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor FlatButton::foregroundColor() const
|
||||||
|
{
|
||||||
|
if (!foreground_color_.isValid()) {
|
||||||
|
if (bg_mode_ == Qt::OpaqueMode) {
|
||||||
|
return ThemeManager::instance().themeColor("BrightWhite");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (role_) {
|
||||||
|
case ui::Primary:
|
||||||
|
return ThemeManager::instance().themeColor("Blue");
|
||||||
|
case ui::Secondary:
|
||||||
|
return ThemeManager::instance().themeColor("Gray");
|
||||||
|
case ui::Default:
|
||||||
|
default:
|
||||||
|
return ThemeManager::instance().themeColor("Black");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return foreground_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setBackgroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
background_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor FlatButton::backgroundColor() const
|
||||||
|
{
|
||||||
|
if (!background_color_.isValid()) {
|
||||||
|
switch (role_) {
|
||||||
|
case ui::Primary:
|
||||||
|
return ThemeManager::instance().themeColor("Blue");
|
||||||
|
case ui::Secondary:
|
||||||
|
return ThemeManager::instance().themeColor("Gray");
|
||||||
|
case ui::Default:
|
||||||
|
default:
|
||||||
|
return ThemeManager::instance().themeColor("Black");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return background_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setOverlayColor(const QColor &color)
|
||||||
|
{
|
||||||
|
overlay_color_ = color;
|
||||||
|
setOverlayStyle(ui::TintedOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor FlatButton::overlayColor() const
|
||||||
|
{
|
||||||
|
if (!overlay_color_.isValid()) {
|
||||||
|
return foregroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlay_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setDisabledForegroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
disabled_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor FlatButton::disabledForegroundColor() const
|
||||||
|
{
|
||||||
|
if (!disabled_color_.isValid()) {
|
||||||
|
return ThemeManager::instance().themeColor("FadedWhite");
|
||||||
|
}
|
||||||
|
|
||||||
|
return disabled_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setDisabledBackgroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
disabled_background_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor FlatButton::disabledBackgroundColor() const
|
||||||
|
{
|
||||||
|
if (!disabled_background_color_.isValid()) {
|
||||||
|
return ThemeManager::instance().themeColor("FadedWhite");
|
||||||
|
}
|
||||||
|
|
||||||
|
return disabled_background_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setFontSize(qreal size)
|
||||||
|
{
|
||||||
|
font_size_ = size;
|
||||||
|
|
||||||
|
QFont f(font());
|
||||||
|
f.setPointSizeF(size);
|
||||||
|
setFont(f);
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal FlatButton::fontSize() const
|
||||||
|
{
|
||||||
|
return font_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setHaloVisible(bool visible)
|
||||||
|
{
|
||||||
|
halo_visible_ = visible;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlatButton::isHaloVisible() const
|
||||||
|
{
|
||||||
|
return halo_visible_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setOverlayStyle(ui::OverlayStyle style)
|
||||||
|
{
|
||||||
|
overlay_style_ = style;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::OverlayStyle FlatButton::overlayStyle() const
|
||||||
|
{
|
||||||
|
return overlay_style_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setRippleStyle(ui::RippleStyle style)
|
||||||
|
{
|
||||||
|
ripple_style_ = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::RippleStyle FlatButton::rippleStyle() const
|
||||||
|
{
|
||||||
|
return ripple_style_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setIconPlacement(ui::ButtonIconPlacement placement)
|
||||||
|
{
|
||||||
|
icon_placement_ = placement;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::ButtonIconPlacement FlatButton::iconPlacement() const
|
||||||
|
{
|
||||||
|
return icon_placement_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setCornerRadius(qreal radius)
|
||||||
|
{
|
||||||
|
corner_radius_ = radius;
|
||||||
|
updateClipPath();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal FlatButton::cornerRadius() const
|
||||||
|
{
|
||||||
|
return corner_radius_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setBackgroundMode(Qt::BGMode mode)
|
||||||
|
{
|
||||||
|
bg_mode_ = mode;
|
||||||
|
state_machine_->setupProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::BGMode FlatButton::backgroundMode() const
|
||||||
|
{
|
||||||
|
return bg_mode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setBaseOpacity(qreal opacity)
|
||||||
|
{
|
||||||
|
base_opacity_ = opacity;
|
||||||
|
state_machine_->setupProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal FlatButton::baseOpacity() const
|
||||||
|
{
|
||||||
|
return base_opacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setCheckable(bool value)
|
||||||
|
{
|
||||||
|
state_machine_->updateCheckedStatus();
|
||||||
|
state_machine_->setCheckedOverlayProgress(0);
|
||||||
|
|
||||||
|
QPushButton::setCheckable(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setHasFixedRippleRadius(bool value)
|
||||||
|
{
|
||||||
|
use_fixed_ripple_radius_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlatButton::hasFixedRippleRadius() const
|
||||||
|
{
|
||||||
|
return use_fixed_ripple_radius_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::setFixedRippleRadius(qreal radius)
|
||||||
|
{
|
||||||
|
fixed_ripple_radius_ = radius;
|
||||||
|
setHasFixedRippleRadius(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize FlatButton::sizeHint() const
|
||||||
|
{
|
||||||
|
ensurePolished();
|
||||||
|
|
||||||
|
QSize label(fontMetrics().size(Qt::TextSingleLine, text()));
|
||||||
|
|
||||||
|
int w = 20 + label.width();
|
||||||
|
int h = label.height();
|
||||||
|
|
||||||
|
if (!icon().isNull()) {
|
||||||
|
w += iconSize().width() + FlatButton::IconPadding;
|
||||||
|
h = qMax(h, iconSize().height());
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSize(w, 20 + h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::checkStateSet()
|
||||||
|
{
|
||||||
|
state_machine_->updateCheckedStatus();
|
||||||
|
QPushButton::checkStateSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (ui::NoRipple != ripple_style_) {
|
||||||
|
QPoint pos;
|
||||||
|
qreal radiusEndValue;
|
||||||
|
|
||||||
|
if (ui::CenteredRipple == ripple_style_) {
|
||||||
|
pos = rect().center();
|
||||||
|
} else {
|
||||||
|
pos = event->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_fixed_ripple_radius_) {
|
||||||
|
radiusEndValue = fixed_ripple_radius_;
|
||||||
|
} else {
|
||||||
|
radiusEndValue = static_cast<qreal>(width()) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ripple *ripple = new Ripple(pos);
|
||||||
|
|
||||||
|
ripple->setRadiusEndValue(radiusEndValue);
|
||||||
|
ripple->setOpacityStartValue(0.35);
|
||||||
|
ripple->setColor(foregroundColor());
|
||||||
|
ripple->radiusAnimation()->setDuration(600);
|
||||||
|
ripple->opacityAnimation()->setDuration(1300);
|
||||||
|
|
||||||
|
ripple_overlay_->addRipple(ripple);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QPushButton::mouseReleaseEvent(event);
|
||||||
|
state_machine_->updateCheckedStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QPushButton::resizeEvent(event);
|
||||||
|
updateClipPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
const qreal cr = corner_radius_;
|
||||||
|
|
||||||
|
if (cr > 0) {
|
||||||
|
QPainterPath path;
|
||||||
|
path.addRoundedRect(rect(), cr, cr);
|
||||||
|
|
||||||
|
painter.setClipPath(path);
|
||||||
|
painter.setClipping(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
paintBackground(&painter);
|
||||||
|
paintHalo(&painter);
|
||||||
|
|
||||||
|
painter.setOpacity(1);
|
||||||
|
painter.setClipping(false);
|
||||||
|
|
||||||
|
paintForeground(&painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::paintBackground(QPainter *painter)
|
||||||
|
{
|
||||||
|
const qreal overlayOpacity = state_machine_->overlayOpacity();
|
||||||
|
const qreal checkedProgress = state_machine_->checkedOverlayProgress();
|
||||||
|
|
||||||
|
if (Qt::OpaqueMode == bg_mode_) {
|
||||||
|
QBrush brush;
|
||||||
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
|
||||||
|
if (isEnabled()) {
|
||||||
|
brush.setColor(backgroundColor());
|
||||||
|
} else {
|
||||||
|
brush.setColor(disabledBackgroundColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setOpacity(1);
|
||||||
|
painter->setBrush(brush);
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
painter->drawRect(rect());
|
||||||
|
}
|
||||||
|
|
||||||
|
QBrush brush;
|
||||||
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ui::NoOverlay != overlay_style_) && (overlayOpacity > 0)) {
|
||||||
|
if (ui::TintedOverlay == overlay_style_) {
|
||||||
|
brush.setColor(overlayColor());
|
||||||
|
} else {
|
||||||
|
brush.setColor(Qt::gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setOpacity(overlayOpacity);
|
||||||
|
painter->setBrush(brush);
|
||||||
|
painter->drawRect(rect());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCheckable() && checkedProgress > 0) {
|
||||||
|
const qreal q = Qt::TransparentMode == bg_mode_ ? 0.45 : 0.7;
|
||||||
|
brush.setColor(foregroundColor());
|
||||||
|
painter->setOpacity(q * checkedProgress);
|
||||||
|
painter->setBrush(brush);
|
||||||
|
QRect r(rect());
|
||||||
|
r.setHeight(static_cast<qreal>(r.height()) * checkedProgress);
|
||||||
|
painter->drawRect(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::paintHalo(QPainter *painter)
|
||||||
|
{
|
||||||
|
if (!halo_visible_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const qreal opacity = state_machine_->haloOpacity();
|
||||||
|
const qreal s = state_machine_->haloScaleFactor() * state_machine_->haloSize();
|
||||||
|
const qreal radius = static_cast<qreal>(width()) * s;
|
||||||
|
|
||||||
|
if (isEnabled() && opacity > 0) {
|
||||||
|
QBrush brush;
|
||||||
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
brush.setColor(foregroundColor());
|
||||||
|
painter->setOpacity(opacity);
|
||||||
|
painter->setBrush(brush);
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
const QPointF center = rect().center();
|
||||||
|
painter->drawEllipse(center, radius, radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define COLOR_INTERPOLATE(CH) (1 - progress) * source.CH() + progress *dest.CH()
|
||||||
|
|
||||||
|
void FlatButton::paintForeground(QPainter *painter)
|
||||||
|
{
|
||||||
|
if (isEnabled()) {
|
||||||
|
painter->setPen(foregroundColor());
|
||||||
|
const qreal progress = state_machine_->checkedOverlayProgress();
|
||||||
|
|
||||||
|
if (isCheckable() && progress > 0) {
|
||||||
|
QColor source = foregroundColor();
|
||||||
|
QColor dest = Qt::TransparentMode == bg_mode_ ? Qt::white
|
||||||
|
: backgroundColor();
|
||||||
|
if (qFuzzyCompare(1, progress)) {
|
||||||
|
painter->setPen(dest);
|
||||||
|
} else {
|
||||||
|
painter->setPen(QColor(COLOR_INTERPOLATE(red),
|
||||||
|
COLOR_INTERPOLATE(green),
|
||||||
|
COLOR_INTERPOLATE(blue),
|
||||||
|
COLOR_INTERPOLATE(alpha)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
painter->setPen(disabledForegroundColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon().isNull()) {
|
||||||
|
painter->drawText(rect(), Qt::AlignCenter, text());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize textSize(fontMetrics().size(Qt::TextSingleLine, text()));
|
||||||
|
QSize base(size() - textSize);
|
||||||
|
|
||||||
|
const int iw = iconSize().width() + IconPadding;
|
||||||
|
QPoint pos((base.width() - iw) / 2, 0);
|
||||||
|
|
||||||
|
QRect textGeometry(pos + QPoint(0, base.height() / 2), textSize);
|
||||||
|
QRect iconGeometry(pos + QPoint(0, (height() - iconSize().height()) / 2), iconSize());
|
||||||
|
|
||||||
|
if (ui::LeftIcon == icon_placement_) {
|
||||||
|
textGeometry.translate(iw, 0);
|
||||||
|
} else {
|
||||||
|
iconGeometry.translate(textSize.width() + IconPadding, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->drawText(textGeometry, Qt::AlignCenter, text());
|
||||||
|
|
||||||
|
QPixmap pixmap = icon().pixmap(iconSize());
|
||||||
|
QPainter icon(&pixmap);
|
||||||
|
icon.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
|
icon.fillRect(pixmap.rect(), painter->pen().color());
|
||||||
|
painter->drawPixmap(iconGeometry, pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButton::updateClipPath()
|
||||||
|
{
|
||||||
|
const qreal radius = corner_radius_;
|
||||||
|
|
||||||
|
QPainterPath path;
|
||||||
|
path.addRoundedRect(rect(), radius, radius);
|
||||||
|
ripple_overlay_->setClipPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatButtonStateMachine::FlatButtonStateMachine(FlatButton *parent)
|
||||||
|
: QStateMachine(parent)
|
||||||
|
, button_(parent)
|
||||||
|
, top_level_state_(new QState(QState::ParallelStates))
|
||||||
|
, config_state_(new QState(top_level_state_))
|
||||||
|
, checkable_state_(new QState(top_level_state_))
|
||||||
|
, checked_state_(new QState(checkable_state_))
|
||||||
|
, unchecked_state_(new QState(checkable_state_))
|
||||||
|
, neutral_state_(new QState(config_state_))
|
||||||
|
, neutral_focused_state_(new QState(config_state_))
|
||||||
|
, hovered_state_(new QState(config_state_))
|
||||||
|
, hovered_focused_state_(new QState(config_state_))
|
||||||
|
, pressed_state_(new QState(config_state_))
|
||||||
|
, halo_animation_(new QSequentialAnimationGroup(this))
|
||||||
|
, overlay_opacity_(0)
|
||||||
|
, checked_overlay_progress_(parent->isChecked() ? 1 : 0)
|
||||||
|
, halo_opacity_(0)
|
||||||
|
, halo_size_(0.8)
|
||||||
|
, halo_scale_factor_(1)
|
||||||
|
, was_checked_(false)
|
||||||
|
{
|
||||||
|
Q_ASSERT(parent);
|
||||||
|
|
||||||
|
parent->installEventFilter(this);
|
||||||
|
|
||||||
|
config_state_->setInitialState(neutral_state_);
|
||||||
|
addState(top_level_state_);
|
||||||
|
setInitialState(top_level_state_);
|
||||||
|
|
||||||
|
checkable_state_->setInitialState(parent->isChecked() ? checked_state_
|
||||||
|
: unchecked_state_);
|
||||||
|
QSignalTransition *transition;
|
||||||
|
QPropertyAnimation *animation;
|
||||||
|
|
||||||
|
transition = new QSignalTransition(this, SIGNAL(buttonChecked()));
|
||||||
|
transition->setTargetState(checked_state_);
|
||||||
|
unchecked_state_->addTransition(transition);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "checkedOverlayProgress", this);
|
||||||
|
animation->setDuration(200);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
transition = new QSignalTransition(this, SIGNAL(buttonUnchecked()));
|
||||||
|
transition->setTargetState(unchecked_state_);
|
||||||
|
checked_state_->addTransition(transition);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "checkedOverlayProgress", this);
|
||||||
|
animation->setDuration(200);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
addTransition(button_, QEvent::FocusIn, neutral_state_, neutral_focused_state_);
|
||||||
|
addTransition(button_, QEvent::FocusOut, neutral_focused_state_, neutral_state_);
|
||||||
|
addTransition(button_, QEvent::Enter, neutral_state_, hovered_state_);
|
||||||
|
addTransition(button_, QEvent::Leave, hovered_state_, neutral_state_);
|
||||||
|
addTransition(button_, QEvent::Enter, neutral_focused_state_, hovered_focused_state_);
|
||||||
|
addTransition(button_, QEvent::Leave, hovered_focused_state_, neutral_focused_state_);
|
||||||
|
addTransition(button_, QEvent::FocusIn, hovered_state_, hovered_focused_state_);
|
||||||
|
addTransition(button_, QEvent::FocusOut, hovered_focused_state_, hovered_state_);
|
||||||
|
addTransition(this, SIGNAL(buttonPressed()), hovered_state_, pressed_state_);
|
||||||
|
addTransition(button_, QEvent::Leave, pressed_state_, neutral_focused_state_);
|
||||||
|
addTransition(button_, QEvent::FocusOut, pressed_state_, hovered_state_);
|
||||||
|
|
||||||
|
neutral_state_->assignProperty(this, "haloSize", 0);
|
||||||
|
neutral_focused_state_->assignProperty(this, "haloSize", 0.7);
|
||||||
|
hovered_state_->assignProperty(this, "haloSize", 0);
|
||||||
|
pressed_state_->assignProperty(this, "haloSize", 4);
|
||||||
|
hovered_focused_state_->assignProperty(this, "haloSize", 0.7);
|
||||||
|
|
||||||
|
QPropertyAnimation *grow = new QPropertyAnimation(this);
|
||||||
|
QPropertyAnimation *shrink = new QPropertyAnimation(this);
|
||||||
|
|
||||||
|
grow->setTargetObject(this);
|
||||||
|
grow->setPropertyName("haloScaleFactor");
|
||||||
|
grow->setStartValue(0.56);
|
||||||
|
grow->setEndValue(0.63);
|
||||||
|
grow->setEasingCurve(QEasingCurve::InOutSine);
|
||||||
|
grow->setDuration(840);
|
||||||
|
|
||||||
|
shrink->setTargetObject(this);
|
||||||
|
shrink->setPropertyName("haloScaleFactor");
|
||||||
|
shrink->setStartValue(0.63);
|
||||||
|
shrink->setEndValue(0.56);
|
||||||
|
shrink->setEasingCurve(QEasingCurve::InOutSine);
|
||||||
|
shrink->setDuration(840);
|
||||||
|
|
||||||
|
halo_animation_->addAnimation(grow);
|
||||||
|
halo_animation_->addAnimation(shrink);
|
||||||
|
halo_animation_->setLoopCount(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatButtonStateMachine::~FlatButtonStateMachine()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::setOverlayOpacity(qreal opacity)
|
||||||
|
{
|
||||||
|
overlay_opacity_ = opacity;
|
||||||
|
button_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::setCheckedOverlayProgress(qreal opacity)
|
||||||
|
{
|
||||||
|
checked_overlay_progress_ = opacity;
|
||||||
|
button_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::setHaloOpacity(qreal opacity)
|
||||||
|
{
|
||||||
|
halo_opacity_ = opacity;
|
||||||
|
button_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::setHaloSize(qreal size)
|
||||||
|
{
|
||||||
|
halo_size_ = size;
|
||||||
|
button_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::setHaloScaleFactor(qreal factor)
|
||||||
|
{
|
||||||
|
halo_scale_factor_ = factor;
|
||||||
|
button_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::startAnimations()
|
||||||
|
{
|
||||||
|
halo_animation_->start();
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::setupProperties()
|
||||||
|
{
|
||||||
|
QColor overlayColor;
|
||||||
|
|
||||||
|
if (Qt::TransparentMode == button_->backgroundMode()) {
|
||||||
|
overlayColor = button_->backgroundColor();
|
||||||
|
} else {
|
||||||
|
overlayColor = button_->foregroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
const qreal baseOpacity = button_->baseOpacity();
|
||||||
|
|
||||||
|
neutral_state_->assignProperty(this, "overlayOpacity", 0);
|
||||||
|
neutral_state_->assignProperty(this, "haloOpacity", 0);
|
||||||
|
neutral_focused_state_->assignProperty(this, "overlayOpacity", 0);
|
||||||
|
neutral_focused_state_->assignProperty(this, "haloOpacity", baseOpacity);
|
||||||
|
hovered_state_->assignProperty(this, "overlayOpacity", baseOpacity);
|
||||||
|
hovered_state_->assignProperty(this, "haloOpacity", 0);
|
||||||
|
hovered_focused_state_->assignProperty(this, "overlayOpacity", baseOpacity);
|
||||||
|
hovered_focused_state_->assignProperty(this, "haloOpacity", baseOpacity);
|
||||||
|
pressed_state_->assignProperty(this, "overlayOpacity", baseOpacity);
|
||||||
|
pressed_state_->assignProperty(this, "haloOpacity", 0);
|
||||||
|
checked_state_->assignProperty(this, "checkedOverlayProgress", 1);
|
||||||
|
unchecked_state_->assignProperty(this, "checkedOverlayProgress", 0);
|
||||||
|
|
||||||
|
button_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::updateCheckedStatus()
|
||||||
|
{
|
||||||
|
const bool checked = button_->isChecked();
|
||||||
|
if (was_checked_ != checked) {
|
||||||
|
was_checked_ = checked;
|
||||||
|
if (checked) {
|
||||||
|
emit buttonChecked();
|
||||||
|
} else {
|
||||||
|
emit buttonUnchecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlatButtonStateMachine::eventFilter(QObject *watched,
|
||||||
|
QEvent *event)
|
||||||
|
{
|
||||||
|
if (QEvent::FocusIn == event->type()) {
|
||||||
|
QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
|
||||||
|
if (focusEvent && Qt::MouseFocusReason == focusEvent->reason()) {
|
||||||
|
emit buttonPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QStateMachine::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::addTransition(QObject *object,
|
||||||
|
const char *signal,
|
||||||
|
QState *fromState,
|
||||||
|
QState *toState)
|
||||||
|
{
|
||||||
|
addTransition(new QSignalTransition(object, signal), fromState, toState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::addTransition(QObject *object,
|
||||||
|
QEvent::Type eventType,
|
||||||
|
QState *fromState,
|
||||||
|
QState *toState)
|
||||||
|
{
|
||||||
|
addTransition(new QEventTransition(object, eventType), fromState, toState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlatButtonStateMachine::addTransition(QAbstractTransition *transition,
|
||||||
|
QState *fromState,
|
||||||
|
QState *toState)
|
||||||
|
{
|
||||||
|
transition->setTargetState(toState);
|
||||||
|
|
||||||
|
QPropertyAnimation *animation;
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "overlayOpacity", this);
|
||||||
|
animation->setDuration(150);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "haloOpacity", this);
|
||||||
|
animation->setDuration(170);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "haloSize", this);
|
||||||
|
animation->setDuration(350);
|
||||||
|
animation->setEasingCurve(QEasingCurve::OutCubic);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
fromState->addTransition(transition);
|
||||||
|
}
|
59
src/ui/OverlayWidget.cc
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#include "OverlayWidget.h"
|
||||||
|
#include <QEvent>
|
||||||
|
|
||||||
|
OverlayWidget::OverlayWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
if (parent)
|
||||||
|
parent->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
OverlayWidget::~OverlayWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverlayWidget::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (!parent())
|
||||||
|
return QWidget::event(event);
|
||||||
|
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::ParentChange: {
|
||||||
|
parent()->installEventFilter(this);
|
||||||
|
setGeometry(overlayGeometry());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::ParentAboutToChange: {
|
||||||
|
parent()->removeEventFilter(this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverlayWidget::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::Move:
|
||||||
|
case QEvent::Resize:
|
||||||
|
setGeometry(overlayGeometry());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect OverlayWidget::overlayGeometry() const
|
||||||
|
{
|
||||||
|
QWidget *widget = parentWidget();
|
||||||
|
|
||||||
|
if (!widget)
|
||||||
|
return QRect();
|
||||||
|
|
||||||
|
return widget->rect();
|
||||||
|
}
|
92
src/ui/RaisedButton.cc
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#include <QEventTransition>
|
||||||
|
#include <QGraphicsDropShadowEffect>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
|
#include <QState>
|
||||||
|
#include <QStateMachine>
|
||||||
|
|
||||||
|
#include "RaisedButton.h"
|
||||||
|
|
||||||
|
void RaisedButton::init()
|
||||||
|
{
|
||||||
|
shadow_state_machine_ = new QStateMachine(this);
|
||||||
|
normal_state_ = new QState;
|
||||||
|
pressed_state_ = new QState;
|
||||||
|
effect_ = new QGraphicsDropShadowEffect;
|
||||||
|
|
||||||
|
effect_->setBlurRadius(7);
|
||||||
|
effect_->setOffset(QPointF(0, 2));
|
||||||
|
effect_->setColor(QColor(0, 0, 0, 75));
|
||||||
|
|
||||||
|
setBackgroundMode(Qt::OpaqueMode);
|
||||||
|
setMinimumHeight(42);
|
||||||
|
setGraphicsEffect(effect_);
|
||||||
|
setBaseOpacity(0.3);
|
||||||
|
|
||||||
|
shadow_state_machine_->addState(normal_state_);
|
||||||
|
shadow_state_machine_->addState(pressed_state_);
|
||||||
|
|
||||||
|
normal_state_->assignProperty(effect_, "offset", QPointF(0, 2));
|
||||||
|
normal_state_->assignProperty(effect_, "blurRadius", 7);
|
||||||
|
|
||||||
|
pressed_state_->assignProperty(effect_, "offset", QPointF(0, 5));
|
||||||
|
pressed_state_->assignProperty(effect_, "blurRadius", 29);
|
||||||
|
|
||||||
|
QAbstractTransition *transition;
|
||||||
|
|
||||||
|
transition = new QEventTransition(this, QEvent::MouseButtonPress);
|
||||||
|
transition->setTargetState(pressed_state_);
|
||||||
|
normal_state_->addTransition(transition);
|
||||||
|
|
||||||
|
transition = new QEventTransition(this, QEvent::MouseButtonDblClick);
|
||||||
|
transition->setTargetState(pressed_state_);
|
||||||
|
normal_state_->addTransition(transition);
|
||||||
|
|
||||||
|
transition = new QEventTransition(this, QEvent::MouseButtonRelease);
|
||||||
|
transition->setTargetState(normal_state_);
|
||||||
|
pressed_state_->addTransition(transition);
|
||||||
|
|
||||||
|
QPropertyAnimation *animation;
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(effect_, "offset", this);
|
||||||
|
animation->setDuration(100);
|
||||||
|
shadow_state_machine_->addDefaultAnimation(animation);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(effect_, "blurRadius", this);
|
||||||
|
animation->setDuration(100);
|
||||||
|
shadow_state_machine_->addDefaultAnimation(animation);
|
||||||
|
|
||||||
|
shadow_state_machine_->setInitialState(normal_state_);
|
||||||
|
shadow_state_machine_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
RaisedButton::RaisedButton(QWidget *parent)
|
||||||
|
: FlatButton(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
RaisedButton::RaisedButton(const QString &text, QWidget *parent)
|
||||||
|
: FlatButton(parent)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
RaisedButton::~RaisedButton()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RaisedButton::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (QEvent::EnabledChange == event->type()) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
shadow_state_machine_->start();
|
||||||
|
effect_->setEnabled(true);
|
||||||
|
} else {
|
||||||
|
shadow_state_machine_->stop();
|
||||||
|
effect_->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatButton::event(event);
|
||||||
|
}
|
106
src/ui/Ripple.cc
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "Ripple.h"
|
||||||
|
#include "RippleOverlay.h"
|
||||||
|
|
||||||
|
Ripple::Ripple(const QPoint ¢er, QObject *parent)
|
||||||
|
: QParallelAnimationGroup(parent)
|
||||||
|
, overlay_(0)
|
||||||
|
, radius_anim_(animate("radius"))
|
||||||
|
, opacity_anim_(animate("opacity"))
|
||||||
|
, radius_(0)
|
||||||
|
, opacity_(0)
|
||||||
|
, center_(center)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ripple::Ripple(const QPoint ¢er, RippleOverlay *overlay, QObject *parent)
|
||||||
|
: QParallelAnimationGroup(parent)
|
||||||
|
, overlay_(overlay)
|
||||||
|
, radius_anim_(animate("radius"))
|
||||||
|
, opacity_anim_(animate("opacity"))
|
||||||
|
, radius_(0)
|
||||||
|
, opacity_(0)
|
||||||
|
, center_(center)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ripple::~Ripple()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ripple::setRadius(qreal radius)
|
||||||
|
{
|
||||||
|
Q_ASSERT(overlay_);
|
||||||
|
|
||||||
|
if (radius_ == radius)
|
||||||
|
return;
|
||||||
|
|
||||||
|
radius_ = radius;
|
||||||
|
overlay_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ripple::setOpacity(qreal opacity)
|
||||||
|
{
|
||||||
|
Q_ASSERT(overlay_);
|
||||||
|
|
||||||
|
if (opacity_ == opacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
opacity_ = opacity;
|
||||||
|
overlay_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ripple::setColor(const QColor &color)
|
||||||
|
{
|
||||||
|
if (brush_.color() == color)
|
||||||
|
return;
|
||||||
|
|
||||||
|
brush_.setColor(color);
|
||||||
|
|
||||||
|
if (overlay_)
|
||||||
|
overlay_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ripple::setBrush(const QBrush &brush)
|
||||||
|
{
|
||||||
|
brush_ = brush;
|
||||||
|
|
||||||
|
if (overlay_)
|
||||||
|
overlay_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ripple::destroy()
|
||||||
|
{
|
||||||
|
Q_ASSERT(overlay_);
|
||||||
|
|
||||||
|
overlay_->removeRipple(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPropertyAnimation *Ripple::animate(const QByteArray &property,
|
||||||
|
const QEasingCurve &easing,
|
||||||
|
int duration)
|
||||||
|
{
|
||||||
|
QPropertyAnimation *animation = new QPropertyAnimation;
|
||||||
|
animation->setTargetObject(this);
|
||||||
|
animation->setPropertyName(property);
|
||||||
|
animation->setEasingCurve(easing);
|
||||||
|
animation->setDuration(duration);
|
||||||
|
|
||||||
|
addAnimation(animation);
|
||||||
|
|
||||||
|
return animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ripple::init()
|
||||||
|
{
|
||||||
|
setOpacityStartValue(0.5);
|
||||||
|
setOpacityEndValue(0);
|
||||||
|
setRadiusStartValue(0);
|
||||||
|
setRadiusEndValue(300);
|
||||||
|
|
||||||
|
brush_.setColor(Qt::black);
|
||||||
|
brush_.setStyle(Qt::SolidPattern);
|
||||||
|
|
||||||
|
connect(this, SIGNAL(finished()), this, SLOT(destroy()));
|
||||||
|
}
|
61
src/ui/RippleOverlay.cc
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "Ripple.h"
|
||||||
|
#include "RippleOverlay.h"
|
||||||
|
|
||||||
|
RippleOverlay::RippleOverlay(QWidget *parent)
|
||||||
|
: OverlayWidget(parent)
|
||||||
|
, use_clip_(false)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
RippleOverlay::~RippleOverlay()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RippleOverlay::addRipple(Ripple *ripple)
|
||||||
|
{
|
||||||
|
ripple->setOverlay(this);
|
||||||
|
ripples_.push_back(ripple);
|
||||||
|
ripple->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RippleOverlay::addRipple(const QPoint &position, qreal radius)
|
||||||
|
{
|
||||||
|
Ripple *ripple = new Ripple(position);
|
||||||
|
ripple->setRadiusEndValue(radius);
|
||||||
|
addRipple(ripple);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RippleOverlay::removeRipple(Ripple *ripple)
|
||||||
|
{
|
||||||
|
if (ripples_.removeOne(ripple))
|
||||||
|
delete ripple;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RippleOverlay::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
if (use_clip_)
|
||||||
|
painter.setClipPath(clip_path_);
|
||||||
|
|
||||||
|
for (auto it = ripples_.constBegin(); it != ripples_.constEnd(); it++)
|
||||||
|
paintRipple(&painter, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RippleOverlay::paintRipple(QPainter *painter, Ripple *ripple)
|
||||||
|
{
|
||||||
|
const qreal radius = ripple->radius();
|
||||||
|
const QPointF center = ripple->center();
|
||||||
|
|
||||||
|
painter->setOpacity(ripple->opacity());
|
||||||
|
painter->setBrush(ripple->brush());
|
||||||
|
painter->drawEllipse(center, radius, radius);
|
||||||
|
}
|
346
src/ui/TextField.cc
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
#include "TextField.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QEventTransition>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
|
|
||||||
|
TextField::TextField(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
state_machine_ = new TextFieldStateMachine(this);
|
||||||
|
label_ = 0;
|
||||||
|
label_font_size_ = 9.5;
|
||||||
|
show_label_ = false;
|
||||||
|
background_color_ = QColor("white");
|
||||||
|
|
||||||
|
setFrame(false);
|
||||||
|
setAttribute(Qt::WA_Hover);
|
||||||
|
setMouseTracking(true);
|
||||||
|
setTextMargins(0, 2, 0, 4);
|
||||||
|
|
||||||
|
QFontDatabase db;
|
||||||
|
QFont font(db.font("Open Sans", "Regular", 11));
|
||||||
|
setFont(font);
|
||||||
|
|
||||||
|
state_machine_->start();
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField::~TextField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setBackgroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
background_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TextField::backgroundColor() const
|
||||||
|
{
|
||||||
|
return background_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setShowLabel(bool value)
|
||||||
|
{
|
||||||
|
if (show_label_ == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
show_label_ = value;
|
||||||
|
|
||||||
|
if (!label_ && value) {
|
||||||
|
label_ = new TextFieldLabel(this);
|
||||||
|
state_machine_->setLabel(label_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
setContentsMargins(0, 23, 0, 0);
|
||||||
|
} else {
|
||||||
|
setContentsMargins(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextField::hasLabel() const
|
||||||
|
{
|
||||||
|
return show_label_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setLabelFontSize(qreal size)
|
||||||
|
{
|
||||||
|
label_font_size_ = size;
|
||||||
|
|
||||||
|
if (label_) {
|
||||||
|
QFont font(label_->font());
|
||||||
|
font.setPointSizeF(size);
|
||||||
|
label_->setFont(font);
|
||||||
|
label_->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal TextField::labelFontSize() const
|
||||||
|
{
|
||||||
|
return label_font_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setLabel(const QString &label)
|
||||||
|
{
|
||||||
|
label_text_ = label;
|
||||||
|
setShowLabel(true);
|
||||||
|
label_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TextField::label() const
|
||||||
|
{
|
||||||
|
return label_text_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setTextColor(const QColor &color)
|
||||||
|
{
|
||||||
|
text_color_ = color;
|
||||||
|
setStyleSheet(QString("QLineEdit { color: %1; }").arg(color.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TextField::textColor() const
|
||||||
|
{
|
||||||
|
if (!text_color_.isValid()) {
|
||||||
|
return QColor("black");
|
||||||
|
}
|
||||||
|
|
||||||
|
return text_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setLabelColor(const QColor &color)
|
||||||
|
{
|
||||||
|
label_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TextField::labelColor() const
|
||||||
|
{
|
||||||
|
if (!label_color_.isValid()) {
|
||||||
|
return QColor("#abb"); // TODO: Move this into Theme.h
|
||||||
|
}
|
||||||
|
|
||||||
|
return label_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setInkColor(const QColor &color)
|
||||||
|
{
|
||||||
|
ink_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TextField::inkColor() const
|
||||||
|
{
|
||||||
|
if (!ink_color_.isValid()) {
|
||||||
|
return QColor("black");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ink_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::setUnderlineColor(const QColor &color)
|
||||||
|
{
|
||||||
|
underline_color_ = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TextField::underlineColor() const
|
||||||
|
{
|
||||||
|
if (!underline_color_.isValid()) {
|
||||||
|
return QColor("black");
|
||||||
|
}
|
||||||
|
|
||||||
|
return underline_color_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextField::event(QEvent *event)
|
||||||
|
{
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::Resize:
|
||||||
|
case QEvent::Move: {
|
||||||
|
if (label_)
|
||||||
|
label_->setGeometry(rect());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLineEdit::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextField::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
QLineEdit::paintEvent(event);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
|
||||||
|
if (text().isEmpty()) {
|
||||||
|
painter.setOpacity(1 - state_machine_->progress());
|
||||||
|
//painter.fillRect(rect(), parentWidget()->palette().color(backgroundRole()));
|
||||||
|
painter.fillRect(rect(), backgroundColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
const int y = height() - 1;
|
||||||
|
const int wd = width() - 5;
|
||||||
|
|
||||||
|
QPen pen;
|
||||||
|
pen.setWidth(1);
|
||||||
|
pen.setColor(underlineColor());
|
||||||
|
painter.setPen(pen);
|
||||||
|
painter.setOpacity(1);
|
||||||
|
painter.drawLine(2.5, y, wd, y);
|
||||||
|
|
||||||
|
QBrush brush;
|
||||||
|
brush.setStyle(Qt::SolidPattern);
|
||||||
|
brush.setColor(inkColor());
|
||||||
|
|
||||||
|
const qreal progress = state_machine_->progress();
|
||||||
|
|
||||||
|
if (progress > 0) {
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(brush);
|
||||||
|
const int w = (1 - progress) * static_cast<qreal>(wd / 2);
|
||||||
|
painter.drawRect(w + 2.5, height() - 2, wd - 2 * w, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldStateMachine::TextFieldStateMachine(TextField *parent)
|
||||||
|
: QStateMachine(parent), text_field_(parent)
|
||||||
|
{
|
||||||
|
normal_state_ = new QState;
|
||||||
|
focused_state_ = new QState;
|
||||||
|
|
||||||
|
label_ = 0;
|
||||||
|
offset_anim_ = 0;
|
||||||
|
color_anim_ = 0;
|
||||||
|
progress_ = 0.0;
|
||||||
|
|
||||||
|
addState(normal_state_);
|
||||||
|
addState(focused_state_);
|
||||||
|
|
||||||
|
setInitialState(normal_state_);
|
||||||
|
|
||||||
|
QEventTransition *transition;
|
||||||
|
QPropertyAnimation *animation;
|
||||||
|
|
||||||
|
transition = new QEventTransition(parent, QEvent::FocusIn);
|
||||||
|
transition->setTargetState(focused_state_);
|
||||||
|
normal_state_->addTransition(transition);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "progress", this);
|
||||||
|
animation->setEasingCurve(QEasingCurve::InCubic);
|
||||||
|
animation->setDuration(310);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
transition = new QEventTransition(parent, QEvent::FocusOut);
|
||||||
|
transition->setTargetState(normal_state_);
|
||||||
|
focused_state_->addTransition(transition);
|
||||||
|
|
||||||
|
animation = new QPropertyAnimation(this, "progress", this);
|
||||||
|
animation->setEasingCurve(QEasingCurve::OutCubic);
|
||||||
|
animation->setDuration(310);
|
||||||
|
transition->addAnimation(animation);
|
||||||
|
|
||||||
|
normal_state_->assignProperty(this, "progress", 0);
|
||||||
|
focused_state_->assignProperty(this, "progress", 1);
|
||||||
|
|
||||||
|
setupProperties();
|
||||||
|
|
||||||
|
connect(text_field_, SIGNAL(textChanged(QString)), this, SLOT(setupProperties()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldStateMachine::~TextFieldStateMachine()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextFieldStateMachine::setLabel(TextFieldLabel *label)
|
||||||
|
{
|
||||||
|
if (label_) {
|
||||||
|
delete label_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset_anim_) {
|
||||||
|
removeDefaultAnimation(offset_anim_);
|
||||||
|
delete offset_anim_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color_anim_) {
|
||||||
|
removeDefaultAnimation(color_anim_);
|
||||||
|
delete color_anim_;
|
||||||
|
}
|
||||||
|
|
||||||
|
label_ = label;
|
||||||
|
|
||||||
|
if (label_) {
|
||||||
|
offset_anim_ = new QPropertyAnimation(label_, "offset", this);
|
||||||
|
offset_anim_->setDuration(210);
|
||||||
|
offset_anim_->setEasingCurve(QEasingCurve::OutCubic);
|
||||||
|
addDefaultAnimation(offset_anim_);
|
||||||
|
|
||||||
|
color_anim_ = new QPropertyAnimation(label_, "color", this);
|
||||||
|
color_anim_->setDuration(210);
|
||||||
|
addDefaultAnimation(color_anim_);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextFieldStateMachine::setupProperties()
|
||||||
|
{
|
||||||
|
if (label_) {
|
||||||
|
const int m = text_field_->textMargins().top();
|
||||||
|
|
||||||
|
if (text_field_->text().isEmpty()) {
|
||||||
|
normal_state_->assignProperty(label_, "offset", QPointF(0, 26));
|
||||||
|
} else {
|
||||||
|
normal_state_->assignProperty(label_, "offset", QPointF(0, 0 - m));
|
||||||
|
}
|
||||||
|
|
||||||
|
focused_state_->assignProperty(label_, "offset", QPointF(0, 0 - m));
|
||||||
|
focused_state_->assignProperty(label_, "color", text_field_->inkColor());
|
||||||
|
normal_state_->assignProperty(label_, "color", text_field_->labelColor());
|
||||||
|
|
||||||
|
if (0 != label_->offset().y() && !text_field_->text().isEmpty()) {
|
||||||
|
label_->setOffset(QPointF(0, 0 - m));
|
||||||
|
} else if (!text_field_->hasFocus() && label_->offset().y() <= 0 && text_field_->text().isEmpty()) {
|
||||||
|
label_->setOffset(QPointF(0, 26));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_field_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldLabel::TextFieldLabel(TextField *parent)
|
||||||
|
: QWidget(parent), text_field_(parent)
|
||||||
|
{
|
||||||
|
x_ = 0;
|
||||||
|
y_ = 26;
|
||||||
|
scale_ = 1;
|
||||||
|
color_ = parent->labelColor();
|
||||||
|
|
||||||
|
QFontDatabase db;
|
||||||
|
QFont font(db.font("Open Sans", "Medium", parent->labelFontSize()));
|
||||||
|
font.setLetterSpacing(QFont::PercentageSpacing, 102);
|
||||||
|
setFont(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldLabel::~TextFieldLabel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextFieldLabel::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
if (!text_field_->hasLabel())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter.scale(scale_, scale_);
|
||||||
|
painter.setPen(color_);
|
||||||
|
painter.setOpacity(1);
|
||||||
|
|
||||||
|
QPointF pos(2 + x_, height() - 36 + y_);
|
||||||
|
painter.drawText(pos.x(), pos.y(), text_field_->label());
|
||||||
|
}
|
73
src/ui/Theme.cc
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "Theme.h"
|
||||||
|
|
||||||
|
Theme::Theme(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
setColor("Black", ui::Color::Black);
|
||||||
|
|
||||||
|
setColor("BrightWhite", ui::Color::BrightWhite);
|
||||||
|
setColor("FadedWhite", ui::Color::FadedWhite);
|
||||||
|
setColor("MediumWhite", ui::Color::MediumWhite);
|
||||||
|
|
||||||
|
setColor("BrightGreen", ui::Color::BrightGreen);
|
||||||
|
setColor("DarkGreen", ui::Color::DarkGreen);
|
||||||
|
setColor("LightGreen", ui::Color::LightGreen);
|
||||||
|
|
||||||
|
setColor("Gray", ui::Color::Gray);
|
||||||
|
setColor("Red", ui::Color::Red);
|
||||||
|
setColor("Blue", ui::Color::Blue);
|
||||||
|
|
||||||
|
setColor("Transparent", ui::Color::Transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Theme::~Theme()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Theme::rgba(int r, int g, int b, qreal a) const
|
||||||
|
{
|
||||||
|
QColor color(r, g, b);
|
||||||
|
color.setAlphaF(a);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Theme::getColor(const QString &key) const
|
||||||
|
{
|
||||||
|
if (!colors_.contains(key)) {
|
||||||
|
qWarning() << "Color with key" << key << "could not be found";
|
||||||
|
return QColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors_.value(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Theme::setColor(const QString &key, const QColor &color)
|
||||||
|
{
|
||||||
|
colors_.insert(key, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Theme::setColor(const QString &key, ui::Color &color)
|
||||||
|
{
|
||||||
|
static const QColor palette[] = {
|
||||||
|
QColor("#171919"),
|
||||||
|
|
||||||
|
QColor("#EBEBEB"),
|
||||||
|
QColor("#C9C9C9"),
|
||||||
|
QColor("#929292"),
|
||||||
|
|
||||||
|
QColor("#1C3133"),
|
||||||
|
QColor("#577275"),
|
||||||
|
QColor("#46A451"),
|
||||||
|
|
||||||
|
QColor("#5D6565"),
|
||||||
|
QColor("#E22826"),
|
||||||
|
QColor("#81B3A9"),
|
||||||
|
|
||||||
|
rgba(0, 0, 0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
colors_.insert(key, palette[color]);
|
||||||
|
}
|
20
src/ui/ThemeManager.cc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <QFontDatabase>
|
||||||
|
|
||||||
|
#include "ThemeManager.h"
|
||||||
|
|
||||||
|
ThemeManager::ThemeManager()
|
||||||
|
{
|
||||||
|
setTheme(new Theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::setTheme(Theme *theme)
|
||||||
|
{
|
||||||
|
theme_ = theme;
|
||||||
|
theme_->setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ThemeManager::themeColor(const QString &key) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(theme_);
|
||||||
|
return theme_->getColor(key);
|
||||||
|
}
|