@ -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 |
@ -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 |
||||
|
@ -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) |
@ -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>. |
@ -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> |
@ -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> |
@ -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> |
@ -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
|
@ -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 |
@ -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
|
@ -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
|
@ -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 |
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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 |
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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. |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 952 B |
After Width: | Height: | Size: 225 B |
After Width: | Height: | Size: 590 B |
After Width: | Height: | Size: 453 B |
After Width: | Height: | Size: 939 B |
After Width: | Height: | Size: 799 B |
After Width: | Height: | Size: 799 B |
After Width: | Height: | Size: 730 B |
@ -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> |
@ -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; |
||||
} |
@ -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(); |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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); |
||||
} |
@ -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); |
||||
} |
@ -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(); |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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_; |
||||
} |
@ -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); |
||||
} |
@ -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(); |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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; |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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); |
||||
} |
@ -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); |
||||
} |
@ -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"; |
||||
} |
||||
} |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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); |
||||
} |
@ -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() |
||||
{ |
||||
} |
@ -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(); |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
@ -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); |
||||
} |
@ -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(); |
||||
} |
@ -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); |
||||
} |
@ -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())); |
||||
} |
@ -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); |
||||
} |
@ -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()); |
||||
} |
@ -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]); |
||||
} |
@ -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); |
||||
} |