Initial commit
This commit is contained in:
674
addons/mantle_darkfated_ultracode/LICENSE
Normal file
674
addons/mantle_darkfated_ultracode/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://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 <https://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:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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
|
||||
<https://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
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
112
addons/mantle_darkfated_ultracode/README.md
Normal file
112
addons/mantle_darkfated_ultracode/README.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Mantle
|
||||
|
||||
🎈 Универсальная библиотека GLua для Garry's Mod: создание интерфейсов и удобные утилиты.
|
||||
|
||||
Весь код снабжён комментариями — изучайте и находите примеры прямо в исходниках.
|
||||
|
||||
## Возможности
|
||||
|
||||
- Кастомные VGUI-элементы
|
||||
- Быстрый рендеринг через RNDX
|
||||
- Загрузка материалов по ссылке
|
||||
- Гибкая система цветовых тем
|
||||
- Уведомления для игроков на сервера
|
||||
- Модульная архитектура
|
||||
- Поддержка кириллицы и UTF-8
|
||||
- Единое меню с документацией и настройками
|
||||
|
||||
## Меню библиотеки
|
||||
|
||||
Имеется меню с документацией и настройками. Для открытия используйте консольную команду: `mantle_menu`.
|
||||
|
||||
## Примеры компонентов
|
||||
|
||||
### Документация и элементы VGUI
|
||||
|
||||
<img width="983" height="720" alt="image" src="https://github.com/user-attachments/assets/892290e4-eb7f-4f65-b306-4ed5cc0c0e5b" />
|
||||
|
||||
### Лёгкий режим окна
|
||||
|
||||
<img width="406" height="314" alt="image" src="https://github.com/user-attachments/assets/31669a2a-f2d3-4e2d-9e82-63a2188fe96e" />
|
||||
|
||||
### ComboBox
|
||||
|
||||
<img width="1002" height="728" alt="image" src="https://github.com/user-attachments/assets/8abb781b-b055-4178-9ee1-20046fbcc409" />
|
||||
|
||||
### SlideBox
|
||||
|
||||
<img width="1021" height="750" alt="image" src="https://github.com/user-attachments/assets/1afae892-4d6c-492b-8ded-38915282c8ee" />
|
||||
|
||||
### Таблицы
|
||||
|
||||
<img width="994" height="729" alt="image" src="https://github.com/user-attachments/assets/bd87ca15-5c25-41a4-9786-034faf71adc9" />
|
||||
|
||||
### Поле ввода
|
||||
|
||||
<img width="994" height="722" alt="image" src="https://github.com/user-attachments/assets/864f6206-f5b8-4072-bd8f-808d4f6f69df" />
|
||||
|
||||
### Всплывающие элементы
|
||||
|
||||
<img width="989" height="720" alt="image" src="https://github.com/user-attachments/assets/a050be2a-727d-450c-84f3-2a04b15a0ceb" />
|
||||
|
||||
### Круговое меню
|
||||
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/268fee97-d54a-4143-8c43-3b9a4e8e272f" />
|
||||
|
||||
### Опциональное меню
|
||||
|
||||
<img width="352" height="323" alt="image" src="https://github.com/user-attachments/assets/2bd0f764-8fcd-4f9e-ba7e-1165c6afb10e" />
|
||||
|
||||
### Цветовые темы
|
||||
|
||||
<img width="996" height="775" alt="image" src="https://github.com/user-attachments/assets/a0b7c168-b773-48f1-b516-f070b76d34f6" />
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/14eaa457-7475-4fe3-9d4e-177035a75fcc" />
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/b0049430-1c08-4534-8075-3bf03201fd85" />
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/5c1ff333-ea6c-408c-aec6-f5f75921cb5d" />
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/e464c564-b7d1-42f6-bdde-6d5130a31acf" />
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/658fb731-aebc-4ce9-8d4d-2e9575961030" />
|
||||
|
||||
### И главное - плавность и магия анимаций
|
||||
|
||||
https://github.com/user-attachments/assets/6a813fd1-6da2-4c59-a84b-f78abfc20900
|
||||
|
||||
## Сторонние примеры
|
||||
|
||||
### Отправка серверных уведомлений
|
||||
|
||||
```lua
|
||||
hook.Add('PlayerSpawn', 'Test', function(pl)
|
||||
Mantle.notify(pl, Color(75, 0, 0), 'Заголовок', 'Привет, ' .. pl:Name() .. '!')
|
||||
-- первым аргументом true, в случае отправки всем игрокам
|
||||
end)
|
||||
```
|
||||
|
||||
### Картинка через ссылку
|
||||
|
||||
```lua
|
||||
http.DownloadMaterial('https://i.imgur.com/eEnGbcp.jpeg', 'dog.png', function(your_mat)
|
||||
hook.Add('HUDPaint', 'Test', function()
|
||||
surface.SetDrawColor(255, 255, 255)
|
||||
surface.SetMaterial(your_mat)
|
||||
surface.DrawTexturedRect(5, 5, 250, 330)
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
### Преобразование символов кириллицы
|
||||
|
||||
```lua
|
||||
hook.Add('HUDPaint', 'test', function()
|
||||
local txt = 'ПриВЕТ МИР Hello World'
|
||||
-- default
|
||||
draw.SimpleText(string.lower(txt), 'Fated.20', 15, 15, color_black)
|
||||
-- mantle
|
||||
draw.SimpleText(utf8.lower(txt), 'Fated.20', 15, 35, color_black)
|
||||
end)
|
||||
```
|
||||
|
||||
<img width="247" height="75" alt="Сравнение default и mantle функции" src="https://github.com/user-attachments/assets/77e0b791-e970-45da-90b3-1b4960b945fd" />
|
||||
|
||||
## Steam Workshop
|
||||
|
||||
Для автообновления – [подпишитесь и добавьте аддон в серверную коллекцию](https://steamcommunity.com/sharedfiles/filedetails/?id=3126986993). Таким образом сможете всегда получать актуальную версию библиотеки ✅
|
||||
@@ -0,0 +1,4 @@
|
||||
RNDX = include('mantle/modules/rndx.lua')
|
||||
|
||||
AddCSLuaFile('mantle/init.lua')
|
||||
include('mantle/init.lua')
|
||||
511
addons/mantle_darkfated_ultracode/lua/mantle/config/colors.lua
Normal file
511
addons/mantle_darkfated_ultracode/lua/mantle/config/colors.lua
Normal file
@@ -0,0 +1,511 @@
|
||||
Mantle.color_dark = {
|
||||
header = Color(40, 40, 40), -- верхняя панель
|
||||
header_text = Color(100, 100, 100), -- цвет элементов в заголовке
|
||||
background = Color(25, 25, 25), -- фон
|
||||
background_alpha = Color(25, 25, 25, 210), -- фон с прозрачностью
|
||||
background_panelpopup = Color(20, 20, 20, 150), -- фон для DermaMenu
|
||||
|
||||
button = Color(54, 54, 54), -- кнопка
|
||||
button_shadow = Color(0, 0, 0, 25), -- тень кнопки для градиента
|
||||
button_hovered = Color(60, 60, 62), -- кнопка при наведении
|
||||
|
||||
category = Color(50, 50, 50), -- категория
|
||||
category_opened = Color(50, 50, 50, 0), -- категория открыта
|
||||
|
||||
theme = Color(106, 108, 197), -- тема интерфейса
|
||||
|
||||
panel = { -- варианты цветов для панели
|
||||
Color(60, 60, 60),
|
||||
Color(50, 50, 50),
|
||||
Color(80, 80, 80)
|
||||
},
|
||||
|
||||
toggle = Color(56, 56, 56), -- тумблер
|
||||
|
||||
focus_panel = Color(46, 46, 46), -- универсальный цвет для элементов
|
||||
hover = Color(60, 65, 80), -- универсальное выделение
|
||||
|
||||
window_shadow = Color(0, 0, 0, 100), -- тень окна
|
||||
|
||||
gray = Color(150, 150, 150, 220),
|
||||
text = Color(255, 255, 255)
|
||||
}
|
||||
Mantle.color_dark.panel_alpha = { -- прозрачные панели
|
||||
ColorAlpha(Mantle.color_dark.panel[1], 150),
|
||||
ColorAlpha(Mantle.color_dark.panel[2], 150),
|
||||
ColorAlpha(Mantle.color_dark.panel[3], 150)
|
||||
}
|
||||
|
||||
-- Тёмная палитра (монотонная)
|
||||
|
||||
Mantle.color_dark_mono = table.Copy(Mantle.color_dark)
|
||||
Mantle.color_dark_mono.theme = Color(121, 121, 121)
|
||||
|
||||
-- Светлая палитра
|
||||
Mantle.color_light = {
|
||||
header = Color(240, 240, 240),
|
||||
header_text = Color(150, 150, 150),
|
||||
background = Color(255, 255, 255),
|
||||
background_alpha = Color(255, 255, 255, 170),
|
||||
background_panelpopup = Color(245, 245, 245, 150),
|
||||
|
||||
button = Color(235, 235, 235),
|
||||
button_shadow = Color(0, 0, 0, 15),
|
||||
button_hovered = Color(196, 199, 218),
|
||||
|
||||
category = Color(240, 240, 245),
|
||||
category_opened = Color(240, 240, 245, 0),
|
||||
|
||||
theme = Color(106, 108, 197),
|
||||
|
||||
panel = {
|
||||
Color(250, 250, 255),
|
||||
Color(240, 240, 245),
|
||||
Color(230, 230, 235)
|
||||
},
|
||||
|
||||
toggle = Color(220, 220, 230),
|
||||
|
||||
focus_panel = Color(245, 245, 255),
|
||||
hover = Color(235, 240, 255),
|
||||
|
||||
window_shadow = Color(0, 0, 0, 50),
|
||||
|
||||
gray = Color(130, 130, 130, 220),
|
||||
text = Color(20, 20, 20)
|
||||
}
|
||||
Mantle.color_light.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_light.panel[1], 120),
|
||||
ColorAlpha(Mantle.color_light.panel[2], 120),
|
||||
ColorAlpha(Mantle.color_light.panel[3], 120)
|
||||
}
|
||||
|
||||
-- Синяя палитра
|
||||
Mantle.color_blue = {
|
||||
header = Color(36, 48, 66),
|
||||
header_text = Color(109, 129, 159),
|
||||
background = Color(24, 28, 38),
|
||||
background_alpha = Color(24, 28, 38, 210),
|
||||
background_panelpopup = Color(20, 24, 32, 150),
|
||||
|
||||
button = Color(38, 54, 82),
|
||||
button_shadow = Color(18, 22, 32, 35),
|
||||
button_hovered = Color(47, 69, 110),
|
||||
|
||||
category = Color(34, 48, 72),
|
||||
category_opened = Color(34, 48, 72, 0),
|
||||
|
||||
theme = Color(80, 160, 220),
|
||||
|
||||
panel = {
|
||||
Color(34, 48, 72),
|
||||
Color(38, 54, 82),
|
||||
Color(70, 120, 180)
|
||||
},
|
||||
|
||||
toggle = Color(34, 44, 66),
|
||||
|
||||
focus_panel = Color(48, 72, 90),
|
||||
hover = Color(80, 160, 220, 90),
|
||||
|
||||
window_shadow = Color(18, 22, 32, 100),
|
||||
|
||||
gray = Color(150, 170, 190, 200),
|
||||
text = Color(210, 220, 235)
|
||||
}
|
||||
Mantle.color_blue.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_blue.panel[1], 110),
|
||||
ColorAlpha(Mantle.color_blue.panel[2], 110),
|
||||
ColorAlpha(Mantle.color_blue.panel[3], 110)
|
||||
}
|
||||
|
||||
-- Красная палитра
|
||||
Mantle.color_red = {
|
||||
header = Color(54, 36, 36),
|
||||
header_text = Color(159, 109, 109),
|
||||
background = Color(32, 24, 24),
|
||||
background_alpha = Color(32, 24, 24, 210),
|
||||
background_panelpopup = Color(28, 20, 20, 150),
|
||||
|
||||
button = Color(66, 38, 38),
|
||||
button_shadow = Color(32, 18, 18, 35),
|
||||
button_hovered = Color(97, 50, 50),
|
||||
|
||||
category = Color(62, 34, 34),
|
||||
category_opened = Color(62, 34, 34, 0),
|
||||
|
||||
theme = Color(180, 80, 80),
|
||||
|
||||
panel = {
|
||||
Color(62, 34, 34),
|
||||
Color(66, 38, 38),
|
||||
Color(140, 70, 70)
|
||||
},
|
||||
|
||||
toggle = Color(60, 34, 34),
|
||||
|
||||
focus_panel = Color(72, 48, 48),
|
||||
hover = Color(180, 80, 80, 90),
|
||||
|
||||
window_shadow = Color(32, 18, 18, 100),
|
||||
|
||||
gray = Color(180, 150, 150, 200),
|
||||
text = Color(235, 210, 210)
|
||||
}
|
||||
Mantle.color_red.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_red.panel[1], 110),
|
||||
ColorAlpha(Mantle.color_red.panel[2], 110),
|
||||
ColorAlpha(Mantle.color_red.panel[3], 110)
|
||||
}
|
||||
|
||||
-- Зелёная палитра
|
||||
Mantle.color_green = {
|
||||
header = Color(36, 54, 40),
|
||||
header_text = Color(109, 159, 109),
|
||||
background = Color(24, 32, 26),
|
||||
background_alpha = Color(24, 32, 26, 210),
|
||||
background_panelpopup = Color(20, 28, 22, 150),
|
||||
|
||||
button = Color(38, 66, 48),
|
||||
button_shadow = Color(18, 32, 22, 35),
|
||||
button_hovered = Color(48, 88, 62),
|
||||
|
||||
category = Color(34, 62, 44),
|
||||
category_opened = Color(34, 62, 44, 0),
|
||||
|
||||
theme = Color(80, 180, 120),
|
||||
|
||||
panel = {
|
||||
Color(34, 62, 44),
|
||||
Color(38, 66, 48),
|
||||
Color(70, 140, 90)
|
||||
},
|
||||
|
||||
toggle = Color(34, 60, 44),
|
||||
|
||||
focus_panel = Color(48, 72, 58),
|
||||
hover = Color(80, 180, 120, 90),
|
||||
|
||||
window_shadow = Color(18, 32, 22, 100),
|
||||
|
||||
gray = Color(150, 180, 150, 200),
|
||||
text = Color(210, 235, 210)
|
||||
}
|
||||
Mantle.color_green.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_green.panel[1], 110),
|
||||
ColorAlpha(Mantle.color_green.panel[2], 110),
|
||||
ColorAlpha(Mantle.color_green.panel[3], 110)
|
||||
}
|
||||
|
||||
-- Оранжевая палитра
|
||||
Mantle.color_orange = {
|
||||
header = Color(70, 35, 10),
|
||||
header_text = Color(250, 230, 210),
|
||||
background = Color(255, 250, 240),
|
||||
background_alpha = Color(255, 250, 240, 220),
|
||||
background_panelpopup = Color(255, 245, 235, 160),
|
||||
|
||||
button = Color(184, 122, 64),
|
||||
button_shadow = Color(20, 10, 0, 30),
|
||||
button_hovered = Color(197, 129, 65),
|
||||
|
||||
category = Color(255, 245, 235),
|
||||
category_opened = Color(255, 245, 235, 0),
|
||||
|
||||
theme = Color(245, 130, 50),
|
||||
|
||||
panel = {
|
||||
Color(255, 250, 240),
|
||||
Color(250, 220, 180),
|
||||
Color(235, 150, 90)
|
||||
},
|
||||
|
||||
toggle = Color(143, 121, 104),
|
||||
|
||||
focus_panel = Color(255, 240, 225),
|
||||
hover = Color(255, 165, 80, 90),
|
||||
|
||||
window_shadow = Color(20, 8, 0, 100),
|
||||
gray = Color(180, 161, 150, 200),
|
||||
text = Color(45, 20, 10)
|
||||
}
|
||||
|
||||
Mantle.color_orange.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_orange.panel[1], 120),
|
||||
ColorAlpha(Mantle.color_orange.panel[2], 120),
|
||||
ColorAlpha(Mantle.color_orange.panel[3], 120)
|
||||
}
|
||||
|
||||
-- Фиолетовая палитра
|
||||
Mantle.color_purple = {
|
||||
header = Color(40, 36, 56),
|
||||
header_text = Color(150, 140, 180),
|
||||
background = Color(25, 22, 30),
|
||||
background_alpha = Color(25, 22, 30, 210),
|
||||
background_panelpopup = Color(28, 24, 40, 150),
|
||||
|
||||
button = Color(58, 52, 76),
|
||||
button_shadow = Color(8, 6, 20, 30),
|
||||
button_hovered = Color(74, 64, 105),
|
||||
|
||||
category = Color(46, 40, 60),
|
||||
category_opened = Color(46, 40, 60, 0),
|
||||
|
||||
theme = Color(138, 114, 219),
|
||||
|
||||
panel = {
|
||||
Color(56, 48, 76),
|
||||
Color(44, 36, 64),
|
||||
Color(120, 90, 200)
|
||||
},
|
||||
|
||||
toggle = Color(43, 39, 53),
|
||||
|
||||
focus_panel = Color(48, 42, 62),
|
||||
hover = Color(138, 114, 219, 90),
|
||||
|
||||
window_shadow = Color(8, 6, 20, 100),
|
||||
|
||||
gray = Color(140, 128, 148, 220),
|
||||
text = Color(245, 240, 255)
|
||||
}
|
||||
Mantle.color_purple.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_purple.panel[1], 150),
|
||||
ColorAlpha(Mantle.color_purple.panel[2], 150),
|
||||
ColorAlpha(Mantle.color_purple.panel[3], 150)
|
||||
}
|
||||
|
||||
-- Кофейная палитра
|
||||
Mantle.color_coffee = {
|
||||
header = Color(67, 48, 36),
|
||||
header_text = Color(210, 190, 170),
|
||||
|
||||
background = Color(45, 32, 25),
|
||||
background_alpha = Color(45, 32, 25, 215),
|
||||
background_panelpopup = Color(38, 28, 22, 150),
|
||||
|
||||
button = Color(84, 60, 45),
|
||||
button_shadow = Color(20, 10, 5, 40),
|
||||
button_hovered = Color(100, 75, 55),
|
||||
|
||||
category = Color(72, 54, 42),
|
||||
category_opened = Color(72, 54, 42, 0),
|
||||
|
||||
theme = Color(150, 110, 75),
|
||||
|
||||
panel = {
|
||||
Color(68, 50, 40),
|
||||
Color(90, 65, 50),
|
||||
Color(150, 110, 75)
|
||||
},
|
||||
|
||||
toggle = Color(53, 40, 31),
|
||||
|
||||
focus_panel = Color(70, 55, 40),
|
||||
hover = Color(150, 110, 75, 90),
|
||||
|
||||
window_shadow = Color(15, 10, 5, 100),
|
||||
|
||||
gray = Color(180, 150, 130, 200),
|
||||
text = Color(235, 225, 210)
|
||||
}
|
||||
Mantle.color_coffee.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_coffee.panel[1], 110),
|
||||
ColorAlpha(Mantle.color_coffee.panel[2], 110),
|
||||
ColorAlpha(Mantle.color_coffee.panel[3], 110)
|
||||
}
|
||||
|
||||
-- Ледяная палитра
|
||||
Mantle.color_ice = {
|
||||
header = Color(190, 225, 250),
|
||||
header_text = Color(68, 104, 139),
|
||||
background = Color(235, 245, 255),
|
||||
background_alpha = Color(235, 245, 255, 200),
|
||||
background_panelpopup = Color(220, 235, 245, 150),
|
||||
|
||||
button = Color(145, 185, 225),
|
||||
button_shadow = Color(80, 110, 140, 40),
|
||||
button_hovered = Color(170, 210, 255),
|
||||
|
||||
category = Color(200, 225, 245),
|
||||
category_opened = Color(200, 225, 245, 0),
|
||||
|
||||
theme = Color(100, 170, 230),
|
||||
|
||||
panel = {
|
||||
Color(146, 186, 211),
|
||||
Color(107, 157, 190),
|
||||
Color(74, 132, 184)
|
||||
},
|
||||
|
||||
toggle = Color(168, 194, 219),
|
||||
|
||||
focus_panel = Color(205, 230, 245),
|
||||
hover = Color(100, 170, 230, 80),
|
||||
|
||||
window_shadow = Color(60, 100, 140, 100),
|
||||
|
||||
gray = Color(92, 112, 133, 200),
|
||||
text = Color(20, 35, 50)
|
||||
}
|
||||
Mantle.color_ice.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_ice.panel[1], 120),
|
||||
ColorAlpha(Mantle.color_ice.panel[2], 120),
|
||||
ColorAlpha(Mantle.color_ice.panel[3], 120)
|
||||
}
|
||||
|
||||
-- Винная палитра
|
||||
Mantle.color_wine = {
|
||||
header = Color(59, 42, 53),
|
||||
header_text = Color(246, 242, 246),
|
||||
background = Color(31, 23, 22),
|
||||
background_alpha = Color(31, 23, 22, 210),
|
||||
background_panelpopup = Color(36, 28, 28, 150),
|
||||
|
||||
button = Color(79, 50, 60),
|
||||
button_shadow = Color(10, 6, 18, 30),
|
||||
button_hovered = Color(90, 52, 65),
|
||||
|
||||
category = Color(79, 50, 60),
|
||||
category_opened = Color(79, 50, 60, 0),
|
||||
|
||||
theme = Color(148, 61, 91),
|
||||
|
||||
panel = {
|
||||
Color(79, 50, 60),
|
||||
Color(63, 44, 48),
|
||||
Color(160, 85, 143)
|
||||
},
|
||||
|
||||
toggle = Color(63, 40, 47),
|
||||
|
||||
focus_panel = Color(70, 48, 58),
|
||||
hover = Color(192, 122, 217, 90),
|
||||
|
||||
window_shadow = Color(10, 6, 20, 100),
|
||||
|
||||
gray = Color(170, 150, 160, 200),
|
||||
text = Color(246, 242, 246)
|
||||
}
|
||||
Mantle.color_wine.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_wine.panel[1], 150),
|
||||
ColorAlpha(Mantle.color_wine.panel[2], 150),
|
||||
ColorAlpha(Mantle.color_wine.panel[3], 150)
|
||||
}
|
||||
|
||||
-- Фиалковая палитра
|
||||
Mantle.color_violet = {
|
||||
header = Color(49, 50, 68),
|
||||
header_text = Color(238, 244, 255),
|
||||
background = Color(22, 24, 35),
|
||||
background_alpha = Color(22, 24, 35, 210),
|
||||
background_panelpopup = Color(36, 40, 56, 150),
|
||||
|
||||
button = Color(58, 64, 84),
|
||||
button_shadow = Color(8, 6, 18, 30),
|
||||
button_hovered = Color(64, 74, 104),
|
||||
|
||||
category = Color(58, 64, 84),
|
||||
category_opened = Color(58, 64, 84, 0),
|
||||
|
||||
theme = Color(159, 180, 255),
|
||||
|
||||
panel = {
|
||||
Color(58, 64, 84),
|
||||
Color(48, 52, 72),
|
||||
Color(109, 136, 255)
|
||||
},
|
||||
|
||||
toggle = Color(46, 51, 66),
|
||||
|
||||
focus_panel = Color(56, 62, 86),
|
||||
hover = Color(159, 180, 255, 90),
|
||||
|
||||
window_shadow = Color(8, 6, 20, 100),
|
||||
|
||||
gray = Color(147, 147, 184, 200),
|
||||
text = Color(238, 244, 255)
|
||||
}
|
||||
Mantle.color_violet.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_violet.panel[1], 150),
|
||||
ColorAlpha(Mantle.color_violet.panel[2], 150),
|
||||
ColorAlpha(Mantle.color_violet.panel[3], 150)
|
||||
}
|
||||
|
||||
-- Моховая палитра
|
||||
Mantle.color_moss = {
|
||||
header = Color(42, 50, 36),
|
||||
header_text = Color(232, 244, 235),
|
||||
background = Color(14, 16, 12),
|
||||
background_alpha = Color(14, 16, 12, 210),
|
||||
background_panelpopup = Color(24, 28, 22, 150),
|
||||
|
||||
button = Color(64, 82, 60),
|
||||
button_shadow = Color(6, 8, 6, 30),
|
||||
button_hovered = Color(74, 99, 68),
|
||||
|
||||
category = Color(46, 64, 44),
|
||||
category_opened = Color(46, 64, 44, 0),
|
||||
|
||||
theme = Color(110, 160, 90),
|
||||
|
||||
panel = {
|
||||
Color(40, 56, 40),
|
||||
Color(66, 86, 66),
|
||||
Color(110, 160, 90)
|
||||
},
|
||||
|
||||
toggle = Color(35, 44, 34),
|
||||
|
||||
focus_panel = Color(46, 58, 44),
|
||||
hover = Color(110, 160, 90, 90),
|
||||
|
||||
window_shadow = Color(0, 0, 0, 100),
|
||||
|
||||
gray = Color(148, 165, 140, 220),
|
||||
text = Color(232, 244, 235)
|
||||
}
|
||||
Mantle.color_moss.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_moss.panel[1], 150),
|
||||
ColorAlpha(Mantle.color_moss.panel[2], 150),
|
||||
ColorAlpha(Mantle.color_moss.panel[3], 150)
|
||||
}
|
||||
|
||||
-- Коралловая палитра
|
||||
Mantle.color_coral = {
|
||||
header = Color(52, 32, 36),
|
||||
header_text = Color(255, 243, 242),
|
||||
background = Color(18, 14, 16),
|
||||
background_alpha = Color(18, 14, 16, 210),
|
||||
background_panelpopup = Color(30, 22, 24, 150),
|
||||
|
||||
button = Color(116, 66, 61),
|
||||
button_shadow = Color(8, 4, 6, 30),
|
||||
button_hovered = Color(134, 73, 68),
|
||||
|
||||
category = Color(74, 40, 42),
|
||||
category_opened = Color(74, 40, 42, 0),
|
||||
|
||||
theme = Color(255, 120, 90),
|
||||
|
||||
panel = {
|
||||
Color(66, 38, 40),
|
||||
Color(120, 60, 56),
|
||||
Color(240, 120, 90)
|
||||
},
|
||||
|
||||
toggle = Color(58, 39, 37),
|
||||
|
||||
focus_panel = Color(72, 42, 44),
|
||||
hover = Color(255, 120, 90, 90),
|
||||
|
||||
window_shadow = Color(0, 0, 0, 100),
|
||||
|
||||
gray = Color(167, 136, 136, 220),
|
||||
text = Color(255, 243, 242)
|
||||
}
|
||||
Mantle.color_coral.panel_alpha = {
|
||||
ColorAlpha(Mantle.color_coral.panel[1], 150),
|
||||
ColorAlpha(Mantle.color_coral.panel[2], 150),
|
||||
ColorAlpha(Mantle.color_coral.panel[3], 150)
|
||||
}
|
||||
326
addons/mantle_darkfated_ultracode/lua/mantle/core/func.lua
Normal file
326
addons/mantle_darkfated_ultracode/lua/mantle/core/func.lua
Normal file
@@ -0,0 +1,326 @@
|
||||
Mantle.func = {
|
||||
sw = ScrW(),
|
||||
sh = ScrH(),
|
||||
ents_scales = {},
|
||||
}
|
||||
|
||||
local function CreateFonts()
|
||||
local function CreateFont(name, font_name, size)
|
||||
surface.CreateFont(name, {
|
||||
font = font_name,
|
||||
size = size,
|
||||
extended = true
|
||||
})
|
||||
end
|
||||
|
||||
local old_surface_SetFont = surface.SetFont
|
||||
local createdFonts = {
|
||||
['Fated.16'] = true
|
||||
}
|
||||
CreateFont('Fated.16', 'Montserrat Medium', 16)
|
||||
|
||||
function surface.SetFont(font)
|
||||
if type(font) != 'string' then
|
||||
if font == nil then
|
||||
ErrorNoHalt('surface.SetFont called with nil! Using fallback font')
|
||||
old_surface_SetFont('DermaDefault')
|
||||
return
|
||||
end
|
||||
old_surface_SetFont(font)
|
||||
return
|
||||
end
|
||||
|
||||
if !createdFonts[font] and font:match('^Fated%.') then
|
||||
local size, isBold = font:match('^Fated%.(%d+)(b?)$')
|
||||
if size then
|
||||
size = tonumber(size)
|
||||
local fontFamily = isBold == 'b' and 'Montserrat Bold' or 'Montserrat Medium'
|
||||
CreateFont(font, fontFamily, size)
|
||||
createdFonts[font] = true
|
||||
end
|
||||
end
|
||||
|
||||
old_surface_SetFont(font)
|
||||
end
|
||||
end
|
||||
|
||||
local math_sin = math.sin
|
||||
local math_clamp = math.Clamp
|
||||
local math_abs = math.abs
|
||||
|
||||
local function CreateFunc()
|
||||
local mat_blur = Material('pp/blurscreen')
|
||||
|
||||
--[[
|
||||
Отрисовка размытия у панели.
|
||||
Применяется в функциях отрисовки (например Paint)
|
||||
]]--
|
||||
function Mantle.func.blur(panel)
|
||||
local x, y = panel:LocalToScreen(0, 0)
|
||||
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(mat_blur)
|
||||
|
||||
for i = 1, 6 do
|
||||
if !mat_blur:GetFloat('$blur') then
|
||||
mat_blur:SetFloat('$blur', i)
|
||||
mat_blur:Recompute()
|
||||
end
|
||||
|
||||
render.UpdateScreenEffectTexture()
|
||||
|
||||
surface.DrawTexturedRect(-x, -y, Mantle.func.sw, Mantle.func.sh)
|
||||
end
|
||||
end
|
||||
|
||||
local listGradients = {
|
||||
Material('vgui/gradient_up'),
|
||||
Material('vgui/gradient_down'),
|
||||
Material('vgui/gradient-l'),
|
||||
Material('vgui/gradient-r')
|
||||
}
|
||||
|
||||
--[[
|
||||
Отрисовка градиента
|
||||
]]--
|
||||
function Mantle.func.gradient(_x, _y, _w, _h, direction, color_shadow, radius, flags)
|
||||
radius = radius and radius or 0
|
||||
RNDX.DrawMaterial(radius, _x, _y, _w, _h, color_shadow, listGradients[direction], flags)
|
||||
end
|
||||
|
||||
function Mantle.func.sound(path)
|
||||
surface.PlaySound(path or 'mantle/btn_click.ogg')
|
||||
end
|
||||
|
||||
Mantle.func.w_save = {}
|
||||
Mantle.func.h_save = {}
|
||||
|
||||
--[[
|
||||
Получение относительной ширины (на основе 1920)
|
||||
При указании Mantle.func.w(20), 20 будет менять в меньшую сторону или большую в зависимости от ширины экрана
|
||||
]]--
|
||||
function Mantle.func.w(px)
|
||||
if !Mantle.func.w_save[px] then
|
||||
Mantle.func.w_save[px] = px / 1920 * Mantle.func.sw
|
||||
end
|
||||
|
||||
return Mantle.func.w_save[px]
|
||||
end
|
||||
|
||||
--[[
|
||||
Получение относительной высоты (на основе 1080)
|
||||
]]--
|
||||
function Mantle.func.h(px)
|
||||
if !Mantle.func.h_save[px] then
|
||||
Mantle.func.h_save[px] = px / 1080 * Mantle.func.sh
|
||||
end
|
||||
|
||||
return Mantle.func.h_save[px]
|
||||
end
|
||||
|
||||
local function EntText(text, y)
|
||||
surface.SetFont('Fated.40')
|
||||
local tw, th = surface.GetTextSize(text)
|
||||
local bx, by = -tw * 0.5 - 18, y - 12
|
||||
local bw, bh = tw + 36, th + 24
|
||||
|
||||
RNDX().Rect(bx, by, bw, bh - 6)
|
||||
:Radii(16, 16, 0, 0)
|
||||
:Blur()
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
RNDX().Rect(bx, by, bw, bh - 6)
|
||||
:Radii(16, 16, 0, 0)
|
||||
:Color(Mantle.color.background_alpha)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
RNDX().Rect(bx, by + bh - 6, bw, 6)
|
||||
:Radii(0, 0, 16, 16)
|
||||
:Color(Mantle.color.text)
|
||||
:Draw()
|
||||
|
||||
draw.SimpleText(text, 'Fated.40', 0, y - 2, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
--[[
|
||||
Отрисовка текста на Entity.
|
||||
Применяется в функциях отрисовки (например ENT:Draw)
|
||||
]]--
|
||||
function Mantle.func.draw_ent_text(ent, text, posY)
|
||||
local distSqr = EyePos():DistToSqr(ent:GetPos())
|
||||
local maxDist = 380
|
||||
if distSqr > maxDist * maxDist then return end
|
||||
|
||||
local dist = math.sqrt(distSqr)
|
||||
local minDist = 20
|
||||
|
||||
local idx = ent:EntIndex()
|
||||
local prev = Mantle.func.ents_scales[idx] or 0
|
||||
|
||||
local normalized = math.Clamp((maxDist - dist) / math.max(1, (maxDist - minDist)), 0, 1)
|
||||
|
||||
local appearThreshold = 0.8
|
||||
local disappearThreshold = 0.01
|
||||
|
||||
local target
|
||||
if normalized <= disappearThreshold then
|
||||
target = 0
|
||||
elseif normalized >= appearThreshold then
|
||||
target = 1
|
||||
else
|
||||
target = (normalized - disappearThreshold) / (appearThreshold - disappearThreshold)
|
||||
end
|
||||
|
||||
local dt = FrameTime() or 0.016
|
||||
local appearSpeed = 18
|
||||
local disappearSpeed = 12
|
||||
local speed = (target > prev) and appearSpeed or disappearSpeed
|
||||
|
||||
local cur = Mantle.func.approachExp(prev, target, speed, dt)
|
||||
if math.abs(cur - target) < 0.0005 then cur = target end
|
||||
Mantle.func.ents_scales[idx] = cur
|
||||
|
||||
local eased = Mantle.func.easeInOutCubic(cur)
|
||||
local alpha = eased
|
||||
local baseScale = 0.13
|
||||
local camScale = baseScale * math.max(1e-4, eased)
|
||||
|
||||
if eased < 0.01 then
|
||||
surface.SetAlphaMultiplier(1)
|
||||
return
|
||||
end
|
||||
|
||||
local _, max = ent:GetRotatedAABB(ent:OBBMins(), ent:OBBMaxs())
|
||||
local rot = (ent:GetPos() - EyePos()):Angle().yaw - 90
|
||||
local bob = math.sin(CurTime() + idx) / 3 + 0.5
|
||||
local center = ent:LocalToWorld(ent:OBBCenter())
|
||||
|
||||
surface.SetAlphaMultiplier(alpha)
|
||||
cam.Start3D2D(center + Vector(0, 0, math.abs(max.z / 2) + 12 + bob), Angle(0, rot, 90), camScale)
|
||||
EntText(text, posY)
|
||||
cam.End3D2D()
|
||||
surface.SetAlphaMultiplier(1)
|
||||
end
|
||||
|
||||
local scaleFactor = 0.8
|
||||
|
||||
function Mantle.func.animate_appearance(panel, target_w, target_h, duration, alpha_dur, callback, scale_factor)
|
||||
if not IsValid(panel) then return end
|
||||
duration = (duration and duration > 0) and duration or 0.18
|
||||
alpha_dur = (alpha_dur and alpha_dur > 0) and alpha_dur or duration
|
||||
|
||||
local startTime = SysTime()
|
||||
local targetX, targetY = panel:GetPos()
|
||||
|
||||
local initialW = target_w * (scale_factor and scale_factor or scaleFactor)
|
||||
local initialH = target_h * (scale_factor and scale_factor or scaleFactor)
|
||||
local initialX = targetX + (target_w - initialW) / 2
|
||||
local initialY = targetY + (target_h - initialH) / 2
|
||||
|
||||
panel:SetSize(initialW, initialH)
|
||||
panel:SetPos(initialX, initialY)
|
||||
panel:SetAlpha(0)
|
||||
|
||||
local curW, curH = initialW, initialH
|
||||
local curX, curY = initialX, initialY
|
||||
local curA = 0
|
||||
|
||||
local eps = 0.5
|
||||
local alpha_eps = 1
|
||||
|
||||
local speedSize = 3 / math.max(0.0001, duration)
|
||||
local speedAlpha = 3 / math.max(0.0001, alpha_dur)
|
||||
|
||||
panel.Think = function()
|
||||
if not IsValid(panel) then return end
|
||||
|
||||
local dt = FrameTime()
|
||||
|
||||
curW = Mantle.func.approachExp(curW, target_w, speedSize, dt)
|
||||
curH = Mantle.func.approachExp(curH, target_h, speedSize, dt)
|
||||
curX = Mantle.func.approachExp(curX, targetX, speedSize, dt)
|
||||
curY = Mantle.func.approachExp(curY, targetY, speedSize, dt)
|
||||
curA = Mantle.func.approachExp(curA, 255, speedAlpha, dt)
|
||||
|
||||
panel:SetSize(curW, curH)
|
||||
panel:SetPos(curX, curY)
|
||||
panel:SetAlpha(math.floor(curA + 0.5))
|
||||
|
||||
local doneSize = math.abs(curW - target_w) <= eps and math.abs(curH - target_h) <= eps
|
||||
local donePos = math.abs(curX - targetX) <= eps and math.abs(curY - targetY) <= eps
|
||||
local doneAlpha = math.abs(curA - 255) <= alpha_eps
|
||||
|
||||
if doneSize and donePos and doneAlpha then
|
||||
panel:SetSize(target_w, target_h)
|
||||
panel:SetPos(targetX, targetY)
|
||||
panel:SetAlpha(255)
|
||||
panel.Think = nil
|
||||
if callback then callback(panel) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Плавное изменение цвета с одного на другой
|
||||
]]--
|
||||
function Mantle.func.LerpColor(frac, col1, col2)
|
||||
local ft = FrameTime() * frac
|
||||
|
||||
return Color(
|
||||
Lerp(ft, col1.r, col2.r),
|
||||
Lerp(ft, col1.g, col2.g),
|
||||
Lerp(ft, col1.b, col2.b),
|
||||
Lerp(ft, col1.a, col2.a)
|
||||
)
|
||||
end
|
||||
|
||||
--[[
|
||||
Функции анимации
|
||||
]]--
|
||||
function Mantle.func.approachExp(current, target, speed, dt)
|
||||
local t = 1 - math.exp(-speed * dt)
|
||||
return current + (target - current) * t
|
||||
end
|
||||
|
||||
function Mantle.func.easeOutCubic(t)
|
||||
return 1 - (1 - t) * (1 - t) * (1 - t)
|
||||
end
|
||||
|
||||
function Mantle.func.easeInOutCubic(t)
|
||||
if t < 0.5 then
|
||||
return 4 * t * t * t
|
||||
else
|
||||
return 1 - math.pow(-2 * t + 2, 3) / 2
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Умное позиционирование панели относительно экрана
|
||||
]]--
|
||||
function Mantle.func.ClampMenuPosition(panel)
|
||||
if not IsValid(panel) then return end
|
||||
local x, y = panel:GetPos()
|
||||
local w, h = panel:GetSize()
|
||||
local sw, sh = Mantle.func.sw, Mantle.func.sh
|
||||
if x < 5 then x = 5 elseif x + w > sw - 5 then x = sw - 5 - w end
|
||||
if y < 5 then y = 5 elseif y + h > sh - 5 then y = sh - 5 - h end
|
||||
panel:SetPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
CreateFunc()
|
||||
CreateFonts()
|
||||
|
||||
hook.Add('OnScreenSizeChanged', 'Mantle', function()
|
||||
local newW, newH = ScrW(), ScrH()
|
||||
|
||||
if newW != Mantle.func.sw and newH != Mantle.func.sh then
|
||||
Mantle.func.sw, Mantle.func.sh = newW, newH
|
||||
|
||||
Mantle.func.w_save = {}
|
||||
Mantle.func.h_save = {}
|
||||
|
||||
CreateFunc()
|
||||
-- CreateFonts()
|
||||
end
|
||||
end)
|
||||
25
addons/mantle_darkfated_ultracode/lua/mantle/core/lang.lua
Normal file
25
addons/mantle_darkfated_ultracode/lua/mantle/core/lang.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
function Mantle.lang.get(addon, key)
|
||||
if !Mantle.lang.list[addon] then
|
||||
print('Mantle.lang.get: addon "' .. addon .. '" not found!')
|
||||
end
|
||||
|
||||
local lang = GetConVar('gmod_language'):GetString()
|
||||
local langTable = Mantle.lang.list[addon][lang]
|
||||
|
||||
if !Mantle.lang.list[addon][lang] then
|
||||
langTable = Mantle.lang.list[addon][Mantle.lang.default]
|
||||
end
|
||||
|
||||
if !langTable then
|
||||
for _, v in pairs(Mantle.lang.list[addon]) do
|
||||
langTable = v
|
||||
break
|
||||
end
|
||||
if !langTable then
|
||||
print('Mantle.lang.get: addon "' .. addon .. '" has no language tables!')
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
return langTable[key] or key
|
||||
end
|
||||
@@ -0,0 +1,314 @@
|
||||
--[[
|
||||
Старые функции отрисовки ui-элементов
|
||||
]]--
|
||||
|
||||
local color_gray = Color(200, 200, 200)
|
||||
local color_red = Color(255, 50, 50)
|
||||
local mat_close = Material('mantle/close_btn.png')
|
||||
|
||||
function Mantle.ui.frame(s, title, width, height, close_bool, anim_bool)
|
||||
s:SetSize(width, height)
|
||||
s:SetTitle('')
|
||||
s:ShowCloseButton(false)
|
||||
s:DockPadding(6, 30, 6, 6)
|
||||
s.f_title = title
|
||||
s.center_title = ''
|
||||
s.background_alpha = true
|
||||
s.Paint = function(self, w, h)
|
||||
local x, y = self:LocalToScreen()
|
||||
|
||||
BShadows.BeginShadow()
|
||||
draw.RoundedBoxEx(6, x, y, w, 24, Mantle.color.header, true, true)
|
||||
draw.RoundedBoxEx(6, x, y + 24, w, h - 24, s.background_alpha and Mantle.color.background_alpha or Mantle.color.background, false, false, true, true)
|
||||
draw.SimpleText(self.f_title, 'Fated.16', x + 6, y + 4, Mantle.color.text)
|
||||
|
||||
if self.center_title then
|
||||
draw.SimpleText(s.center_title, 'Fated.20b', x + w * 0.5, y + 11, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
BShadows.EndShadow(1, 2, 2, 255, 0, 0)
|
||||
end
|
||||
|
||||
if anim_bool then
|
||||
Mantle.func.animate_appearance(s, width, height, 0.1, 0.2)
|
||||
end
|
||||
|
||||
if close_bool then
|
||||
s.cls = vgui.Create('DButton', s)
|
||||
s.cls:SetSize(20, 20)
|
||||
s.cls:SetPos(width - 22, 2)
|
||||
s.cls:SetText('')
|
||||
s.cls.Paint = function(_, w, h)
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(mat_close)
|
||||
surface.DrawTexturedRect(0, 0, w, h)
|
||||
end
|
||||
s.cls.DoClick = function()
|
||||
s:AlphaTo(0, 0.1, 0, function()
|
||||
s:Remove()
|
||||
end)
|
||||
end
|
||||
s.cls.DoRightClick = function()
|
||||
local DM = Mantle.ui.derma_menu()
|
||||
DM:AddOption('Закрыть окно', function()
|
||||
s:Remove()
|
||||
end, 'icon16/cross.png')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Mantle.ui.sp(s)
|
||||
local vbar = s:GetVBar()
|
||||
vbar:SetWide(12)
|
||||
vbar:SetHideButtons(true)
|
||||
vbar.Paint = nil
|
||||
vbar.btnGrip.Paint = function(self, w, h)
|
||||
if self.Depressed then
|
||||
self:SetCursor('sizens')
|
||||
end
|
||||
|
||||
draw.RoundedBox(6, 6, 0, w - 6, h, Mantle.color.theme)
|
||||
end
|
||||
end
|
||||
|
||||
function Mantle.ui.btn(s, icon, icon_size, col, rad, off_grad_bool, hov_color, off_hov_bool)
|
||||
s:SetTall(32)
|
||||
s.hoverStatus = 0
|
||||
s.btn_font = 'Fated.18'
|
||||
s.Paint = function(self, w, h)
|
||||
if !self.btn_text then
|
||||
self.btn_text = self:GetText()
|
||||
self:SetText('')
|
||||
end
|
||||
|
||||
if self:IsHovered() then
|
||||
self.hoverStatus = math.Clamp(self.hoverStatus + 4 * FrameTime(), 0, 255)
|
||||
else
|
||||
self.hoverStatus = math.Clamp(self.hoverStatus - 8 * FrameTime(), 0, 255)
|
||||
end
|
||||
|
||||
draw.RoundedBox(rad and rad or 6, 0, 0, w, h, col and col or Mantle.color.button)
|
||||
|
||||
if !off_hov_bool then
|
||||
local color_hover = hov_color and hov_color or Mantle.color.button_hovered
|
||||
color_hover = Color(color_hover.r, color_hover.g, color_hover.b, 255 * self.hoverStatus)
|
||||
|
||||
draw.RoundedBox(rad and rad or 6, 0, 0, w, h, color_hover)
|
||||
end
|
||||
|
||||
if !off_grad_bool then
|
||||
Mantle.func.gradient(0, 0, w, h, 1, Mantle.color.button_shadow)
|
||||
end
|
||||
|
||||
draw.SimpleText(self.btn_text, self.btn_font, w * 0.5 + (icon and icon_size * 0.5 - 2 or 0), h * 0.5 - 1, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if icon then
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(icon)
|
||||
|
||||
local indent = (h - icon_size) * 0.5
|
||||
surface.DrawTexturedRect(indent, indent, icon_size, icon_size)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Mantle.ui.slidebox(parent, label, min_value, max_value, convar, decimals)
|
||||
local slider = vgui.Create('DButton', parent)
|
||||
slider:Dock(TOP)
|
||||
slider:DockMargin(0, 6, 0, 0)
|
||||
slider:SetTall(40)
|
||||
slider:SetText('')
|
||||
|
||||
local value = GetConVar(convar):GetFloat()
|
||||
local sections = max_value - min_value
|
||||
local smoothPos = 0
|
||||
local targetPos = 0
|
||||
|
||||
local function UpdateSliderPosition(new_value)
|
||||
local progress = (new_value - min_value) / sections
|
||||
targetPos = (slider:GetWide() - 16) * progress
|
||||
LocalPlayer():ConCommand(convar .. ' ' .. new_value)
|
||||
value = new_value
|
||||
end
|
||||
|
||||
UpdateSliderPosition(value)
|
||||
|
||||
slider.Paint = function(self, w, h)
|
||||
draw.RoundedBox(4, 0, h - 16, w, 6, Mantle.color.panel_alpha[1])
|
||||
|
||||
smoothPos = Lerp(FrameTime() * 10, smoothPos, targetPos)
|
||||
|
||||
draw.RoundedBox(16, smoothPos, 18, 16, 16, Mantle.color.theme)
|
||||
|
||||
draw.SimpleText(label, 'Fated.18', 4, 0, Mantle.color.text)
|
||||
draw.SimpleText(math.Round(value, decimals), 'Fated.18', w - 4, 0, Mantle.color.text, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
local function UpdateSliderByCursorPos(x)
|
||||
local progress = math.Clamp(x / (slider:GetWide() - 16), 0, 1)
|
||||
local new_value = math.Round(min_value + (progress * sections), decimals)
|
||||
UpdateSliderPosition(new_value)
|
||||
end
|
||||
|
||||
slider.OnMousePressed = function(_, mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
UpdateSliderByCursorPos(slider:CursorPos())
|
||||
slider:MouseCapture(true)
|
||||
end
|
||||
end
|
||||
|
||||
slider.OnMouseReleased = function(_, mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
slider:MouseCapture(false)
|
||||
end
|
||||
end
|
||||
|
||||
slider.OnCursorMoved = function(_, x, _)
|
||||
if input.IsMouseDown(MOUSE_LEFT) then
|
||||
UpdateSliderByCursorPos(x)
|
||||
end
|
||||
end
|
||||
|
||||
return slider
|
||||
end
|
||||
|
||||
function Mantle.ui.desc_entry(parent, title, placeholder, off_title_bool)
|
||||
if !off_title_bool and title then
|
||||
local label = vgui.Create('DLabel', parent)
|
||||
label:Dock(TOP)
|
||||
label:DockMargin(4, 0, 4, 0)
|
||||
label:SetText(title)
|
||||
label:SetFont('Fated.16')
|
||||
end
|
||||
|
||||
local entry_background = vgui.Create('DPanel', parent)
|
||||
entry_background:Dock(TOP)
|
||||
entry_background:DockMargin(4, 4, 4, 0)
|
||||
entry_background:SetTall(24)
|
||||
|
||||
local entry = vgui.Create('DTextEntry', entry_background)
|
||||
entry:Dock(FILL)
|
||||
entry:DockMargin(2, 4, 2, 4)
|
||||
entry:SetPlaceholderText(placeholder)
|
||||
entry:SetFont('Fated.16')
|
||||
entry:SetDrawLanguageID(false)
|
||||
entry:SetPaintBackground(false)
|
||||
|
||||
return entry, entry_background
|
||||
end
|
||||
|
||||
function Mantle.ui.checkbox(parent, text, convar)
|
||||
local panel = vgui.Create('DPanel', parent)
|
||||
panel:Dock(TOP)
|
||||
panel:DockMargin(4, 0, 4, 0)
|
||||
panel:SetTall(28)
|
||||
panel.Paint = function(_, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, Mantle.color.panel_alpha[2])
|
||||
draw.SimpleText(text, 'Fated.18', 8, h * 0.5 - 1, Mantle.color.text, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
local option = vgui.Create('DButton', panel)
|
||||
option:Dock(RIGHT)
|
||||
option:SetWide(56)
|
||||
option:SetText('')
|
||||
option.enabled = convar and GetConVar(convar):GetBool() or false
|
||||
option.Paint = function(self, w, h)
|
||||
draw.RoundedBoxEx(6, 0, 0, w, h, Mantle.color.panel_alpha[1], false, true, false, true)
|
||||
draw.SimpleText(self.enabled and 'ВКЛ' or 'ВЫКЛ', 'Fated.19', w * 0.5 - 1, h * 0.5 - 1, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
option.DoClick = function()
|
||||
if convar then
|
||||
RunConsoleCommand(convar, option.enabled and 0 or 1)
|
||||
end
|
||||
|
||||
option.enabled = !option.enabled
|
||||
end
|
||||
|
||||
return panel, option
|
||||
end
|
||||
|
||||
function Mantle.ui.panel_tabs(parent)
|
||||
local panel_tabs = vgui.Create('DPanel', parent)
|
||||
panel_tabs:Dock(FILL)
|
||||
panel_tabs.Paint = nil
|
||||
panel_tabs.content = {}
|
||||
panel_tabs.active_tab = ''
|
||||
|
||||
panel_tabs.sp = vgui.Create('DHorizontalScroller', panel_tabs)
|
||||
panel_tabs.sp:Dock(TOP)
|
||||
panel_tabs.sp:DockMargin(0, 0, 0, 6)
|
||||
panel_tabs.sp:SetTall(24)
|
||||
panel_tabs.sp:SetOverlap(-6)
|
||||
|
||||
panel_tabs.panel_content = vgui.Create('DPanel', panel_tabs)
|
||||
panel_tabs.panel_content:Dock(FILL)
|
||||
panel_tabs.panel_content.Paint = function(_, w, h)
|
||||
if panel_tabs.active_tab == '' then
|
||||
draw.SimpleText('Выберете вкладку', 'Fated.16', w * 0.5, h * 0.5 - panel_tabs.sp:GetTall() - 7, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
function panel_tabs:AddTab(title, panel, icon, col, col_hov)
|
||||
panel_tabs.content[title] = panel
|
||||
panel_tabs.content[title]:SetParent(panel_tabs.panel_content)
|
||||
panel_tabs.content[title]:Dock(FILL)
|
||||
panel_tabs.content[title]:SetVisible(false)
|
||||
|
||||
local btn_tab = vgui.Create('DButton', panel_tabs.sp)
|
||||
surface.SetFont('Fated.20')
|
||||
btn_tab:SetSize(surface.GetTextSize(title) + 10 + (icon and 18 or 0), 20)
|
||||
btn_tab:SetText('')
|
||||
|
||||
if icon then
|
||||
btn_tab.icon = Material(icon)
|
||||
panel_tabs.content[title].icon = icon
|
||||
end
|
||||
|
||||
btn_tab.Paint = function(self, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, panel_tabs.active_tab == title and (col_hov and col_hov or Mantle.color.panel[2]) or (col and col or Mantle.color.theme))
|
||||
|
||||
if self:IsHovered() then
|
||||
draw.RoundedBox(6, 0, 0, w, h, Mantle.color.button_shadow)
|
||||
end
|
||||
|
||||
draw.SimpleText(title, 'Fated.20', w * 0.5 + (self.icon and 9 or 0), 11, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if self.icon then
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(self.icon)
|
||||
surface.DrawTexturedRect(4, 4, 16, 16)
|
||||
end
|
||||
end
|
||||
btn_tab.DoClick = function()
|
||||
panel_tabs:ActiveTab(title)
|
||||
end
|
||||
btn_tab.DoRightClick = function()
|
||||
local DM = Mantle.ui.derma_menu()
|
||||
|
||||
for tab_name, tab in pairs(panel_tabs.content) do
|
||||
DM:AddOption(tab_name, function()
|
||||
panel_tabs:ActiveTab(tab_name)
|
||||
end, tab.icon)
|
||||
end
|
||||
end
|
||||
|
||||
panel_tabs.sp:AddPanel(btn_tab)
|
||||
end
|
||||
|
||||
function panel_tabs:ActiveTab(title)
|
||||
if title == panel_tabs.active_tab then
|
||||
return
|
||||
end
|
||||
|
||||
for tab_title, tab in pairs(panel_tabs.content) do
|
||||
if tab_title != title then
|
||||
tab:SetVisible(false)
|
||||
else
|
||||
tab:SetVisible(true)
|
||||
|
||||
panel_tabs.active_tab = title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return panel_tabs
|
||||
end
|
||||
909
addons/mantle_darkfated_ultracode/lua/mantle/core/menu.lua
Normal file
909
addons/mantle_darkfated_ultracode/lua/mantle/core/menu.lua
Normal file
@@ -0,0 +1,909 @@
|
||||
local function CreateMenu()
|
||||
if IsValid(menuMantle) then
|
||||
menuMantle:Remove()
|
||||
end
|
||||
|
||||
menuMantle = vgui.Create('MantleFrame')
|
||||
menuMantle:SetSize(920, 640)
|
||||
menuMantle:Center()
|
||||
menuMantle:MakePopup()
|
||||
menuMantle:SetTitle('Mantle')
|
||||
menuMantle:SetCenterTitle('Основное меню библиотеки')
|
||||
menuMantle:ShowAnimation()
|
||||
|
||||
local tabs = vgui.Create('MantleTabs', menuMantle)
|
||||
tabs:Dock(FILL)
|
||||
|
||||
local function CreateTabHeader(title, subtitle, icon, pan)
|
||||
local header = vgui.Create('Panel', pan)
|
||||
header:Dock(TOP)
|
||||
header:DockMargin(0, 0, 0, 8)
|
||||
header:SetTall(56)
|
||||
|
||||
header.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(8)
|
||||
:Color(Mantle.color.panel_alpha[2])
|
||||
:Draw()
|
||||
|
||||
RNDX().Rect(12, h * 0.5 - 12, 24, 24)
|
||||
:Color(255, 255, 255)
|
||||
:Material(icon)
|
||||
:Draw()
|
||||
|
||||
draw.SimpleText(title, 'Fated.20', 48, 10, Mantle.color.text)
|
||||
draw.SimpleText(subtitle, 'Fated.16', 48, h - 10, Mantle.color.gray, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
end
|
||||
|
||||
return header
|
||||
end
|
||||
|
||||
local function CreateCopyButton(parent, snippet)
|
||||
local b = vgui.Create('DButton', parent)
|
||||
b:SetText('')
|
||||
b:SetWide(110)
|
||||
b.Paint = function(me, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(6)
|
||||
:Color(Mantle.color.panel_alpha[1])
|
||||
:Draw()
|
||||
|
||||
draw.SimpleText('Скопировать', 'Fated.16', w / 2, h / 2, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
b.DoClick = function()
|
||||
SetClipboardText(snippet)
|
||||
menuMantle:Notify(snippet)
|
||||
Mantle.func.sound()
|
||||
end
|
||||
return b
|
||||
end
|
||||
|
||||
local function CreateInfo(info, pan)
|
||||
local panelInfo = vgui.Create('Panel')
|
||||
panelInfo:Dock(TOP)
|
||||
panelInfo:DockMargin(0, 0, 0, 6)
|
||||
panelInfo:SetTall(50)
|
||||
|
||||
panelInfo.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(6)
|
||||
:Color(Mantle.color.panel_alpha[2])
|
||||
:Draw()
|
||||
|
||||
Mantle.func.gradient(0, 0, 6, h, 3, Mantle.color.theme, 6)
|
||||
|
||||
draw.SimpleText(info[1], 'Fated.20', 16, 7, Mantle.color.text)
|
||||
draw.SimpleText(info[2], 'Fated.16', 16, h - 7, Mantle.color.gray, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
end
|
||||
|
||||
local copyBtn = CreateCopyButton(panelInfo, info[1])
|
||||
copyBtn:Dock(RIGHT)
|
||||
copyBtn:DockMargin(0, 10, 10, 10)
|
||||
|
||||
pan:AddItem(panelInfo)
|
||||
end
|
||||
|
||||
local function CreateCategory(name, info_table, pan, ui_element, is_active)
|
||||
local panel = vgui.Create('MantleCategory', pan)
|
||||
panel:Dock(TOP)
|
||||
panel:DockMargin(0, 0, 0, 6)
|
||||
panel:SetText(name)
|
||||
|
||||
if is_active then
|
||||
panel:SetActive(true)
|
||||
end
|
||||
|
||||
for _, info in ipairs(info_table) do
|
||||
CreateInfo(info, panel)
|
||||
end
|
||||
|
||||
if ui_element then
|
||||
panel:AddItem(ui_element)
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateTabElements()
|
||||
local panel = vgui.Create('MantleScrollPanel')
|
||||
CreateTabHeader('UI Элементы', 'Демонстрация всех компонентов Mantle. Клик по элементу открывает пример.', Material('icon16/chart_pie.png'), panel)
|
||||
|
||||
local menuWide = menuMantle:GetWide()
|
||||
|
||||
-- Кнопка
|
||||
local panelBtns = vgui.Create('Panel')
|
||||
panelBtns:Dock(TOP)
|
||||
panelBtns:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
panelBtns:SetTall(132)
|
||||
|
||||
local btn1 = vgui.Create('MantleBtn', panelBtns)
|
||||
btn1:Dock(TOP)
|
||||
btn1:SetTall(40)
|
||||
btn1:SetTxt('Стандартная кнопка')
|
||||
|
||||
local btn2 = vgui.Create('MantleBtn', panelBtns)
|
||||
btn2:Dock(TOP)
|
||||
btn2:DockMargin(0, 6, 0, 0)
|
||||
btn2:SetTall(40)
|
||||
btn2:SetTxt('Эффект волны')
|
||||
btn2:SetRipple(true)
|
||||
|
||||
local btn3 = vgui.Create('MantleBtn', panelBtns)
|
||||
btn3:Dock(TOP)
|
||||
btn3:DockMargin(0, 6, 0, 0)
|
||||
btn3:SetTall(40)
|
||||
btn3:SetTxt('Кастомный цвет')
|
||||
btn3:SetColor(Color(182, 65, 65))
|
||||
btn3:SetColorHover(Color(143, 57, 57))
|
||||
btn3:SetIcon(Material('icon16/delete.png'), 16)
|
||||
|
||||
CreateCategory('Кнопка (MantleBtn)', {
|
||||
{':SetHover(bool is_hover)', 'Включить/выключить цвет наведения (дефолт - true)'},
|
||||
{':SetFont(string font)', 'Установить шрифт'},
|
||||
{':SetRadius(int rad)', 'Установить размер закругления'},
|
||||
{':SetIcon(string icon, int icon_size)', 'Установить иконку'},
|
||||
{':SetTxt(string text)', 'Установить текст'},
|
||||
{':SetColor(color col)', 'Установить цвет кнопки'},
|
||||
{':SetColorHover(color col)', 'Установить цвет наведения'},
|
||||
{':SetGradient(bool is_grad)', 'Включить/выключить градиент (дефолт - true)'},
|
||||
{':SetRipple(bool is_ripple)', 'Включить/выключить эффект волн (дефолт - false)'}
|
||||
}, panel, panelBtns)
|
||||
|
||||
-- Чекбокс
|
||||
local checkbox = vgui.Create('MantleCheckBox')
|
||||
checkbox:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
checkbox:Dock(TOP)
|
||||
checkbox:SetTxt('Отображение HUD')
|
||||
checkbox:SetConvar('cl_drawhud')
|
||||
CreateCategory('Тумблер (MantleCheckBox)', {
|
||||
{':SetTxt(string text)', 'Установить текст'},
|
||||
{':SetValue(bool value)', 'Установить bool-значение тумблера'},
|
||||
{':GetBool()', 'Получить bool-значение тумблера'},
|
||||
{':SetConvar(string convar)', 'Установить ConVar'},
|
||||
{':SetDescription(string desc)', 'Установить описание для тумблера'},
|
||||
{':OnChange(bool new_value)', 'Вызывается при изменении значения тумблера'}
|
||||
}, panel, checkbox)
|
||||
|
||||
-- Ввод текста
|
||||
local entry = vgui.Create('MantleEntry')
|
||||
entry:Dock(TOP)
|
||||
entry:DockMargin(menuWide * 0.35, 6, menuWide * 0.35, 0)
|
||||
entry:SetTitle('Никнейм')
|
||||
entry:SetPlaceholder('darkf')
|
||||
CreateCategory('Ввод текста (MantleEntry)', {
|
||||
{':SetTitle(string text)', 'Установить заголовок'},
|
||||
{':SetPlaceholder(string text)', 'Установить фоновый текст (появляется при пустом поле)'},
|
||||
{':GetValue()', 'Получить string-значение поля'}
|
||||
}, panel, entry)
|
||||
|
||||
-- Окно
|
||||
local panelFrames = vgui.Create('Panel')
|
||||
panelFrames:Dock(TOP)
|
||||
panelFrames:SetTall(92)
|
||||
|
||||
local btnFrame1 = vgui.Create('MantleBtn', panelFrames)
|
||||
btnFrame1:Dock(TOP)
|
||||
btnFrame1:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
btnFrame1:SetTxt('Обычное окно')
|
||||
btnFrame1:SetTall(40)
|
||||
btnFrame1.DoClick = function()
|
||||
local frame = vgui.Create('MantleFrame')
|
||||
frame:SetSize(400, 300)
|
||||
frame:Center()
|
||||
frame:MakePopup()
|
||||
frame:SetCenterTitle('Центр')
|
||||
end
|
||||
|
||||
local btnFrame2 = vgui.Create('MantleBtn', panelFrames)
|
||||
btnFrame2:Dock(TOP)
|
||||
btnFrame2:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
btnFrame2:SetTxt('Lite-режим')
|
||||
btnFrame2:SetTall(40)
|
||||
btnFrame2.DoClick = function()
|
||||
local frame = vgui.Create('MantleFrame')
|
||||
frame:SetSize(400, 300)
|
||||
frame:Center()
|
||||
frame:MakePopup()
|
||||
frame:LiteMode()
|
||||
end
|
||||
|
||||
CreateCategory('Окно (MantleFrame)', {
|
||||
{':SetAlphaBackground(bool is_alpha)', 'Включить/выключить прозрачность окна (дефолт - false)'},
|
||||
{':SetTitle(string title)', 'Установить заголовок'},
|
||||
{':SetCenterTitle(string title)', 'Установить центральный заголовок'},
|
||||
{':ShowAnimation()', 'Активировать анимацию при появлении меню'},
|
||||
{':DisableCloseBtn()', 'Скрыть кнопку закрытия'},
|
||||
{':SetDraggable(bool is_draggable)', 'Включить/выключить перемещение окна'},
|
||||
{':LiteMode()', 'Активировать режим Lite (без верхней панели)'},
|
||||
{':Notify(string text, number duration, color col)', 'Показать уведомление внизу окна (дефолт времени - 2 сек., цвета - Mantle.color.theme)'}
|
||||
}, panel, panelFrames)
|
||||
|
||||
-- ScrollPanel
|
||||
local sp = vgui.Create('MantleScrollPanel')
|
||||
sp:Dock(TOP)
|
||||
sp:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
sp:SetTall(150)
|
||||
for spK = 1, 10 do
|
||||
local spPanel = vgui.Create('DPanel', sp)
|
||||
spPanel:Dock(TOP)
|
||||
spPanel:DockMargin(0, 0, 0, 6)
|
||||
spPanel:SetTall(24)
|
||||
spPanel.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.panel_alpha[1])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
end
|
||||
CreateCategory('Панель прокрутки (MantleScrollPanel)', {
|
||||
{':SetScroll(number offset)', 'Установить смещение прокрутки'},
|
||||
{':GetScroll()', 'Получить текущее смещение прокрутки'},
|
||||
{':AddItem(object panel)', 'Добавить элемент в панель'},
|
||||
{':Clear()', 'Очистить панель от всего'},
|
||||
{':DisableVBarPadding()', 'Отключить отступ справа для скроллбара (по умолчанию имеется)'}
|
||||
}, panel, sp)
|
||||
|
||||
-- Вкладки
|
||||
local panelTabs = vgui.Create('Panel')
|
||||
panelTabs:Dock(TOP)
|
||||
panelTabs:SetTall(280)
|
||||
|
||||
local testTabs = vgui.Create('MantleTabs', panelTabs) -- modern стиль
|
||||
testTabs:Dock(TOP)
|
||||
testTabs:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
testTabs:SetTall(150)
|
||||
local testTab1 = vgui.Create('DPanel')
|
||||
testTab1.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(53, 98, 40)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
testTabs:AddTab('Test1', testTab1)
|
||||
local testTab2 = vgui.Create('DPanel')
|
||||
testTab2.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(108, 41, 45)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
testTabs:AddTab('Test2', testTab2)
|
||||
|
||||
local testTabs2 = vgui.Create('MantleTabs', panelTabs) -- classic стиль
|
||||
testTabs2:Dock(FILL)
|
||||
testTabs2:DockMargin(menuWide * 0.3, 10, menuWide * 0.3, 0)
|
||||
testTabs2:SetTabStyle('classic')
|
||||
local testTab3 = vgui.Create('DPanel')
|
||||
testTab3.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(51, 61, 116)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
testTabs2:AddTab('Test3', testTab3)
|
||||
local testTab4 = vgui.Create('DPanel')
|
||||
testTab4.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(138, 89, 43)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
testTabs2:AddTab('Test4', testTab4)
|
||||
local testTab5 = vgui.Create('DPanel')
|
||||
testTab5.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(43, 138, 133)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
testTabs2:AddTab('С иконкой', testTab5, Material('icon16/folder.png'))
|
||||
|
||||
CreateCategory('Вкладки (MantleTabs)', {
|
||||
{':SetTabStyle(string style)', 'Установить стиль вкладок (modern или classic)'},
|
||||
{':SetTabHeight(int height)', 'Установить высоту вкладок'},
|
||||
{':SetIndicatorHeight(int height)', 'Установить высоту индикатора вкладок'},
|
||||
{':AddTab(string name, object panel, string icon)', 'Добавить вкладку'}
|
||||
}, panel, panelTabs)
|
||||
|
||||
-- Выбор варианта
|
||||
local combo = vgui.Create('MantleComboBox')
|
||||
combo:SetPlaceholder('Выберите вариант')
|
||||
combo:AddChoice('Вариант 1', 'value1')
|
||||
combo:AddChoice('Вариант 2', 'value2')
|
||||
combo:AddChoice('Вариант 3', 'value3')
|
||||
combo:AddChoice('Вариант 4', 'value4')
|
||||
combo:AddChoice('Вариант 5', 'value5')
|
||||
combo:AddChoice('Вариант 6', 'value6')
|
||||
combo:AddChoice('Вариант 7', 'value7')
|
||||
combo:AddChoice('Вариант 8', 'value8')
|
||||
combo.OnSelect = function(idx, text, data)
|
||||
chat.AddText(color_white, 'Вы выбрали: ', Mantle.color.theme, text, color_white, ' (', tostring(data), ')')
|
||||
end
|
||||
combo:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
combo:Dock(TOP)
|
||||
CreateCategory('Выпадающий список (MantleComboBox)', {
|
||||
{':AddChoice(string text, any data)', 'Добавить вариант в список (data — любое значение, связанное с пунктом)'},
|
||||
{':SetValue(string text)', 'Установить выбранное значение по тексту'},
|
||||
{':GetValue()', 'Получить выбранное значение (текст)'},
|
||||
{':SetPlaceholder(string text)', 'Установить текст-заполнитель (placeholder)'},
|
||||
{':OnSelect(idx, text, data)', 'Вызывается при выборе варианта: idx — индекс, text — текст, data — значение'}
|
||||
}, panel, combo)
|
||||
|
||||
-- Таблица
|
||||
local tableExample = vgui.Create('MantleTable')
|
||||
tableExample:Dock(TOP)
|
||||
tableExample:DockMargin(menuWide * 0.2, 6, menuWide * 0.2, 0)
|
||||
tableExample:SetTall(250)
|
||||
|
||||
tableExample:AddColumn('Название', 200, TEXT_ALIGN_LEFT, true)
|
||||
tableExample:AddColumn('Тип', 120, TEXT_ALIGN_CENTER, true)
|
||||
tableExample:AddColumn('Качество', 100, TEXT_ALIGN_CENTER, true)
|
||||
tableExample:AddColumn('Цена', 110, TEXT_ALIGN_RIGHT, true)
|
||||
|
||||
local products = {
|
||||
{'Молоко "Домик в деревне"', 'Молочка', 'Высшее', 89},
|
||||
{'Хлеб "Бородинский"', 'Выпечка', 'Стандарт', 45},
|
||||
{'Сок "Добрый"', 'Напитки', 'Премиум', 120},
|
||||
{'Шоколад "Аленка"', 'Конфеты', 'Высшее', 95},
|
||||
{'Йогурт "Активиа"', 'Молочка', 'Премиум', 65},
|
||||
{'Пельмени "Сибирские"', 'Заморозка', 'Стандарт', 350},
|
||||
{'Колбаса "Докторская"', 'Мясо', 'Высшее', 450},
|
||||
{'Сыр "Российский"', 'Молочка', 'Стандарт', 380},
|
||||
{'Пицца "Пепперони"', 'Заморозка', 'Премиум', 450},
|
||||
{'Чай "Липтон"', 'Напитки', 'Стандарт', 180},
|
||||
{'Печенье "Юбилейное"', 'Выпечка', 'Стандарт', 85},
|
||||
{'Масло "Крестьянское"', 'Молочка', 'Высшее', 120},
|
||||
{'Сметана "Простоквашино"', 'Молочка', 'Стандарт', 65},
|
||||
{'Курица "Бройлер"', 'Мясо', 'Стандарт', 280},
|
||||
{'Рыба "Минтай"', 'Морепродукты', 'Стандарт', 320},
|
||||
{'Яблоки "Голден"', 'Фрукты', 'Высшее', 180},
|
||||
{'Картофель', 'Овощи', 'Стандарт', 45},
|
||||
{'Морковь', 'Овощи', 'Стандарт', 35},
|
||||
{'Бананы', 'Фрукты', 'Стандарт', 120},
|
||||
{'Апельсины', 'Фрукты', 'Премиум', 180}
|
||||
}
|
||||
|
||||
for _, product in ipairs(products) do
|
||||
tableExample:AddItem(unpack(product))
|
||||
end
|
||||
|
||||
tableExample:SetAction(function(row_data)
|
||||
chat.AddText(color_white, 'Выбран продукт: ', Mantle.color.theme, row_data[1], color_white, ' (', row_data[2], ')')
|
||||
end)
|
||||
|
||||
CreateCategory('Таблица (MantleTable)', {
|
||||
{':AddColumn(string name, number width, number align, bool sortable)', 'Добавить колонку'},
|
||||
{':AddItem(...)', 'Добавить строку. Количество аргументов должно соответствовать количеству колонок'},
|
||||
{':SetAction(function(table row_data))', 'Установить функцию, вызываемую при клике на строку. row_data — массив значений строки'},
|
||||
{':SetRightClickAction(function(table row_data))', 'Установить функцию, вызываемую при правом клике на строку'},
|
||||
{':Clear()', 'Очистить таблицу от всех строк'},
|
||||
{':GetSelectedRow()', 'Получить данные выбранной строки (массив значений)'},
|
||||
{':GetRowCount()', 'Получить количество строк в таблице'},
|
||||
{':RemoveRow(number index)', 'Удалить строку по индексу (начиная с 1)'}
|
||||
}, panel, tableExample)
|
||||
|
||||
-- Категория
|
||||
local panelCat = vgui.Create('Panel')
|
||||
panelCat:Dock(TOP)
|
||||
panelCat:DockMargin(0, 6, 0, 0)
|
||||
panelCat:SetTall(142)
|
||||
panelCat.Paint = nil
|
||||
|
||||
local cat = vgui.Create('MantleCategory', panelCat)
|
||||
cat:Dock(TOP)
|
||||
cat:SetCenterText(true)
|
||||
cat:SetActive(true)
|
||||
local panGreen = vgui.Create('DPanel')
|
||||
panGreen:Dock(TOP)
|
||||
panGreen:SetTall(50)
|
||||
panGreen.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(93, 179, 101)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
cat:AddItem(panGreen)
|
||||
local panRed = vgui.Create('DPanel')
|
||||
panRed:Dock(TOP)
|
||||
panRed:DockMargin(0, 6, 0, 0)
|
||||
panRed:SetTall(50)
|
||||
panRed.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w - 12, h)
|
||||
:Rad(16)
|
||||
:Color(179, 110, 93)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
cat:AddItem(panRed)
|
||||
CreateCategory('Категория (MantleCategory)', {
|
||||
{':SetText(string name)', 'Установить название'},
|
||||
{':AddItem(object panel)', 'Добавить в категорию элемент'},
|
||||
{':SetColor(color col)', 'Установить кастомный цвет категории'},
|
||||
{':SetCenterText(bool is_centered)', 'Установить центрирование названия'},
|
||||
{':SetActive(bool is_active)', 'Установить активность категории (дефолт - false)'}
|
||||
}, panel, panelCat)
|
||||
|
||||
-- Слайдер
|
||||
local slider = vgui.Create('MantleSlideBox')
|
||||
slider:Dock(TOP)
|
||||
slider:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
slider:SetRange(0, 4)
|
||||
slider:SetConvar('net_graph')
|
||||
slider:SetText('График')
|
||||
CreateCategory('Слайдер (MantleSlideBox)', {
|
||||
{':SetRange(int min_value, int max_value, int decimals)', 'Сделать диапазон слайдера с точностью (дефолт точность - 0)'},
|
||||
{':SetConvar(string convar)', 'Установить ConVar'},
|
||||
{':SetText(string text)', 'Установить текстовое обозначение'},
|
||||
{':SetValue(string val)', 'Установить значение'},
|
||||
{':GetValue()', 'Получить выбранное значение (число)'},
|
||||
{':OnValueChanged(string new_value)', 'Вызывается при изменении значения слайдера'}
|
||||
}, panel, slider)
|
||||
|
||||
local panelTexts = vgui.Create('Panel')
|
||||
panelTexts:Dock(TOP)
|
||||
panelTexts:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
panelTexts:DockPadding(8, 8, 8, 8)
|
||||
panelTexts:SetTall(344)
|
||||
panelTexts.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.panel_alpha[2])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local panelText1 = vgui.Create('DPanel', panelTexts)
|
||||
panelText1:Dock(TOP)
|
||||
panelText1:DockMargin(0, 0, 0, 6)
|
||||
panelText1:SetTall(74)
|
||||
panelText1.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.panel_alpha[1])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local text1 = vgui.Create('MantleText', panelText1)
|
||||
text1:Dock(FILL)
|
||||
text1:SetPadding(10)
|
||||
text1:SetText('MantleText — компонент для аккуратного вывода многострочного текста. Текст автоматически переносится по ширине и сокращается троеточием')
|
||||
|
||||
local panelText2 = vgui.Create('DPanel', panelTexts)
|
||||
panelText2:Dock(TOP)
|
||||
panelText2:DockMargin(0, 0, 0, 6)
|
||||
panelText2:SetTall(100)
|
||||
panelText2.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.panel_alpha[1])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local text2 = vgui.Create('MantleText', panelText2)
|
||||
text2:Dock(FILL)
|
||||
text2:SetPadding(12)
|
||||
text2:SetFont('Fated.20')
|
||||
text2:SetText('Центрирование: горизонталь + вертикаль. Текст выровнен по центру блока.')
|
||||
text2:SetAlign(TEXT_ALIGN_CENTER)
|
||||
text2:SetVAlign('center')
|
||||
|
||||
local panelText3 = vgui.Create('DPanel', panelTexts)
|
||||
panelText3:Dock(TOP)
|
||||
panelText3:DockMargin(0, 0, 0, 6)
|
||||
panelText3:SetTall(54)
|
||||
panelText3.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.panel_alpha[1])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local text3 = vgui.Create('MantleText', panelText3)
|
||||
text3:Dock(FILL)
|
||||
text3:SetPadding(8)
|
||||
text3:SetText('ОченьДлинноеСловоБезПробеловКотороеНужноОтделитьЧтобыНеПорвалосьОформление')
|
||||
|
||||
local panelText4 = vgui.Create('DPanel', panelTexts)
|
||||
panelText4:Dock(TOP)
|
||||
panelText4:SetTall(82)
|
||||
panelText4.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.panel_alpha[1])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local longText = [[
|
||||
Это длинный пример текста, который занимает несколько строк. Если блок небольшой по высоте — последняя видимая строка будет усечена с троеточием, чтобы не порвать верстку и не выходить за пределы панели нашего меню.
|
||||
]]
|
||||
|
||||
local text4 = vgui.Create('MantleText', panelText4)
|
||||
text4:Dock(FILL)
|
||||
text4:SetPadding(8)
|
||||
text4:SetFont('Fated.16')
|
||||
text4:SetText(longText)
|
||||
|
||||
CreateCategory('Текст (MantleText)', {
|
||||
{':SetText(string text)', 'Установить текст для отображения'},
|
||||
{':SetFont(string font)', 'Установить шрифт'},
|
||||
{':SetColor(color col)', 'Установить цвет текста'},
|
||||
{':SetAlign(number align)', 'Горизонтальное выравнивание (TEXT_ALIGN_*)'},
|
||||
{':SetVAlign(string valign)', 'Вертикальное выравнивание: top, center, bottom'},
|
||||
{':SetPadding(number px)', 'Внутренний отступ от краёв'}
|
||||
}, panel, panelTexts)
|
||||
|
||||
return panel
|
||||
end
|
||||
|
||||
tabs:AddTab('UI Элементы', CreateTabElements(), Material('icon16/chart_pie.png'))
|
||||
|
||||
local function CreateShowMenus()
|
||||
local panel = vgui.Create('MantleScrollPanel')
|
||||
CreateTabHeader('Всплывающие', 'Палитра, derma-меню, radial и другие утилиты.', Material('icon16/application_double.png'), panel)
|
||||
|
||||
local listMenus = {
|
||||
{'Выбор цвета через палитру', function()
|
||||
Mantle.ui.color_picker(function(col)
|
||||
chat.AddText('Вы выбрали цвет: ', col, tostring(col))
|
||||
end, Color(25, 59, 102))
|
||||
end},
|
||||
{'Опциональное меню (Derma Menu)', function()
|
||||
local DM = Mantle.ui.derma_menu()
|
||||
for i = 1, 5 do
|
||||
DM:AddOption('Опция ' .. i, function()
|
||||
chat.AddText('Привет всем! ' .. i)
|
||||
end)
|
||||
end
|
||||
DM:AddSpacer()
|
||||
DM:AddOption('Узнать свою привилегию', function()
|
||||
chat.AddText(LocalPlayer():GetUserGroup())
|
||||
end, 'icon16/status_online.png')
|
||||
end},
|
||||
{'Опциональное с подменю (Derma Menu)', function()
|
||||
local DM = Mantle.ui.derma_menu()
|
||||
|
||||
local clothes = DM:AddOption('Одежда')
|
||||
local subClothes = clothes:AddSubMenu()
|
||||
subClothes:AddOption('Шапка', function()
|
||||
chat.AddText('Вы выбрали: Шапка')
|
||||
end)
|
||||
subClothes:AddOption('Свитер', function()
|
||||
chat.AddText('Вы выбрали: Свитер')
|
||||
end)
|
||||
|
||||
local food = DM:AddOption('Еда')
|
||||
local subFood = food:AddSubMenu()
|
||||
subFood:AddOption('Морковь', function()
|
||||
chat.AddText('Вы выбрали: Морковь')
|
||||
end)
|
||||
subFood:AddOption('Яблоко', function()
|
||||
chat.AddText('Вы выбрали: Яблоко')
|
||||
end)
|
||||
end},
|
||||
{'Выбор игрока', function()
|
||||
Mantle.ui.player_selector(function(pl)
|
||||
chat.AddText('Вы выбрали игрока: ', color_white, pl:Name())
|
||||
end)
|
||||
end},
|
||||
{'Круговое меню', function()
|
||||
--[[
|
||||
Имеется возможность настроить радиальное меню
|
||||
|
||||
local configRadial = {
|
||||
disable_background = true, -- отключает фон
|
||||
hover_sound = 'buttons/button14.wav', -- звук при наведении
|
||||
scale_animation = false, -- отключает анимацию масштабирования
|
||||
radius = 300, -- радиус меню
|
||||
inner_radius = 100 -- радиус внутреннего круга
|
||||
}
|
||||
|
||||
local rm = Mantle.ui.radial_menu(configRadial)
|
||||
--]]
|
||||
|
||||
local rm = Mantle.ui.radial_menu()
|
||||
rm:SetCenterText('Действия', 'Выберите действие')
|
||||
|
||||
local weaponsMenu = rm:CreateSubMenu('Оружие', 'Выберите оружие')
|
||||
weaponsMenu:AddOption('Пистолет', function()
|
||||
chat.AddText(Mantle.color.theme, 'Выбран пистолет')
|
||||
end, 'icon16/gun.png', 'Обычный пистолет')
|
||||
weaponsMenu:AddOption('Винтовка', function()
|
||||
chat.AddText(Mantle.color.theme, 'Выбрана винтовка')
|
||||
end, 'icon16/gun.png', 'Мощная винтовка')
|
||||
rm:AddSubMenuOption('Оружие', weaponsMenu, 'icon16/gun.png', 'Выберите оружие')
|
||||
|
||||
-- Обычные опции
|
||||
rm:AddOption('Выбросить', function()
|
||||
chat.AddText('Выбросить оружие')
|
||||
end, 'icon16/gun.png', 'Выбросить оружие')
|
||||
rm:AddOption('Кинуть кубик', function()
|
||||
chat.AddText('Действие выполнено')
|
||||
end, 'icon16/controller.png', 'Рандом кубика')
|
||||
rm:AddOption('Погибнуть', function()
|
||||
chat.AddText('Действие выполнено')
|
||||
end, 'icon16/world.png', 'Попрощаться с миром')
|
||||
rm:AddOption('Хакнуть', function()
|
||||
chat.AddText('Действие выполнено')
|
||||
end, 'icon16/server.png', 'Взломать сервер')
|
||||
rm:AddOption('Посмотреть баланс', function()
|
||||
chat.AddText('Действие выполнено')
|
||||
end, 'icon16/money.png', 'Сколько у вас денег')
|
||||
rm:AddOption('Нет иконки', function()
|
||||
chat.AddText('Действие выполнено')
|
||||
end, nil, 'Где иконка?')
|
||||
end},
|
||||
{'Написание текста', function()
|
||||
Mantle.ui.text_box('Заголовок', 'Описание того, что вводиться', function(s)
|
||||
chat.AddText('Вы ввели: ', color_white, s)
|
||||
end)
|
||||
end},
|
||||
{'Вызов сообщения в Окне', function()
|
||||
menuMantle:Notify('Тестовое сообщение!')
|
||||
end}
|
||||
}
|
||||
|
||||
for _, elem in ipairs(listMenus) do
|
||||
local btn = vgui.Create('MantleBtn', panel)
|
||||
btn:Dock(TOP)
|
||||
btn:DockMargin(0, 0, 0, 6)
|
||||
btn:SetTall(30)
|
||||
btn:SetTxt(elem[1])
|
||||
btn.DoClick = function()
|
||||
elem[2]()
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
|
||||
return panel
|
||||
end
|
||||
|
||||
tabs:AddTab('Всплывающие', CreateShowMenus(), Material('icon16/application_double.png'))
|
||||
|
||||
local function CreateTabFunctions()
|
||||
local panel = vgui.Create('MantleScrollPanel')
|
||||
CreateTabHeader('Функции', 'Полный список утилитарных функций Mantle.func и других вспомогательных функций', Material('icon16/cog.png'), panel)
|
||||
|
||||
local menuWide = menuMantle:GetWide()
|
||||
|
||||
CreateCategory('Размытие панели', {
|
||||
{'Mantle.func.blur(object panel)', 'Отрисовка размытия панели в Paint'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Градиент', {
|
||||
{'Mantle.func.gradient(int x, int y, int w, int h, int dir, color color_shadow, int radius, flags)', 'Отрисовка градиента (dir: 1 - вверх, 2 - вниз, 3 - влево, 4 - вправо)'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Создание звука', {
|
||||
{'Mantle.func.sound(string path)', 'Проигрывает звук (дефолт - mantle/btn_click.ogg)'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Относительные единицы для адаптивного интерфейса', {
|
||||
{'Mantle.func.w(int px)', 'Относительная ширина (от 1920)'},
|
||||
{'Mantle.func.h(int px)', 'Относительная высота (от 1080)'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Отрисовка текста над энтити', {
|
||||
{'Mantle.func.draw_ent_text(object ent, string text, int posY)', 'Рисует текст над энтити с плавным появлением (3D2D)'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Анимация размера панели', {
|
||||
{'Mantle.func.animate_appearance(object panel, int w, int h, int duration, int alpha_dur, func callback, int scale_factor)', 'Плавное изменение панели до нужного размера'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Плавное изменение цвета', {
|
||||
{'Mantle.func.LerpColor(int frac, color col1, color col2)', 'Плавный переход цвета от col1 → col2'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Загрузка картинки', {
|
||||
{'http.DownloadMaterial(string url, string path, func callback, int retry_count)', 'Скачивает материал по URL и кэширует его. Повторяет попытку при ошибке, возвращает через callback материал'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Серверное уведомление', {
|
||||
{'Mantle.notify(object pl, color header_color, string header, string text)', 'Отправка сообщений в чат игроку или всем (вместо pl указать true - тогда всем)'}
|
||||
}, panel)
|
||||
|
||||
CreateCategory('Изменение регистра букв', {
|
||||
{'utf8.lower(string text)', 'Преобразует строку в нижний регистр с поддержкой русских букв'},
|
||||
{'utf8.upper(string text)', 'Преобразует строку в верхний регистр с поддержкой русских букв'}
|
||||
}, panel)
|
||||
|
||||
return panel
|
||||
end
|
||||
|
||||
tabs:AddTab('Функции', CreateTabFunctions(), Material('icon16/error.png'))
|
||||
|
||||
local function CreateLegacyTest()
|
||||
local panel = vgui.Create('MantleScrollPanel')
|
||||
CreateTabHeader('Legacy UI', 'Набор legacy-утилит (Mantle.ui.*). Для совместимости и примеров.', Material('icon16/exclamation.png'), panel)
|
||||
|
||||
local menuWide = menuMantle:GetWide()
|
||||
|
||||
local btnFrame = vgui.Create('MantleBtn')
|
||||
btnFrame:SetTxt('Открыть Legacy Frame')
|
||||
btnFrame:SetTall(40)
|
||||
btnFrame:DockMargin(menuWide * 0.3, 6, menuWide * 0.3, 0)
|
||||
btnFrame:Dock(TOP)
|
||||
btnFrame.DoClick = function()
|
||||
local frame = vgui.Create('DFrame')
|
||||
frame:SetSize(400, 300)
|
||||
frame:Center()
|
||||
frame:MakePopup()
|
||||
Mantle.ui.frame(frame, 'Legacy Frame', 400, 300, true, true)
|
||||
|
||||
local scroll = vgui.Create('DScrollPanel', frame)
|
||||
scroll:Dock(FILL)
|
||||
Mantle.ui.sp(scroll)
|
||||
|
||||
-- Тест кнопок с разными параметрами
|
||||
local btn1 = vgui.Create('DButton', scroll)
|
||||
btn1:Dock(TOP)
|
||||
btn1:DockMargin(10, 10, 10, 0)
|
||||
btn1:SetText('Обычная кнопка')
|
||||
Mantle.ui.btn(btn1)
|
||||
|
||||
local btn2 = vgui.Create('DButton', scroll)
|
||||
btn2:Dock(TOP)
|
||||
btn2:DockMargin(10, 10, 10, 0)
|
||||
btn2:SetText('Кнопка с иконкой')
|
||||
Mantle.ui.btn(btn2, Material('icon16/accept.png'), 16)
|
||||
|
||||
local btn3 = vgui.Create('DButton', scroll)
|
||||
btn3:Dock(TOP)
|
||||
btn3:DockMargin(10, 10, 10, 0)
|
||||
btn3:SetText('Кнопка без градиента')
|
||||
Mantle.ui.btn(btn3, nil, nil, nil, nil, true)
|
||||
|
||||
local btn4 = vgui.Create('DButton', scroll)
|
||||
btn4:Dock(TOP)
|
||||
btn4:DockMargin(10, 10, 10, 0)
|
||||
btn4:SetText('Кнопка без ховера')
|
||||
Mantle.ui.btn(btn4, nil, nil, nil, nil, nil, nil, true)
|
||||
|
||||
-- Тест слайдеров
|
||||
local slider1 = Mantle.ui.slidebox(scroll, 'Слайдер (0-100)', 0, 100, 'net_graph', 0)
|
||||
slider1:DockMargin(10, 20, 10, 0)
|
||||
|
||||
local slider2 = Mantle.ui.slidebox(scroll, 'Слайдер (0-1)', 0, 1, 'cl_drawhud', 2)
|
||||
slider2:DockMargin(10, 20, 10, 0)
|
||||
|
||||
-- Тест полей ввода
|
||||
local entry1, entry_bg1 = Mantle.ui.desc_entry(scroll, 'Поле с заголовком', 'Введите текст...')
|
||||
entry_bg1:DockMargin(10, 20, 10, 0)
|
||||
|
||||
local entry2, entry_bg2 = Mantle.ui.desc_entry(scroll, nil, 'Поле без заголовка')
|
||||
entry_bg2:DockMargin(10, 20, 10, 0)
|
||||
|
||||
-- Тест чекбоксов
|
||||
local checkbox1, checkbox_btn1 = Mantle.ui.checkbox(scroll, 'Чекбокс с ConVar', 'cl_drawhud')
|
||||
checkbox1:DockMargin(10, 20, 10, 0)
|
||||
|
||||
local checkbox2, checkbox_btn2 = Mantle.ui.checkbox(scroll, 'Чекбокс без ConVar')
|
||||
checkbox2:DockMargin(10, 20, 10, 0)
|
||||
|
||||
-- Тест вкладок
|
||||
local panelTabs = vgui.Create('DPanel', scroll)
|
||||
panelTabs:Dock(TOP)
|
||||
panelTabs:SetTall(250)
|
||||
panelTabs.Paint = nil
|
||||
|
||||
local tabs = Mantle.ui.panel_tabs(panelTabs)
|
||||
tabs:DockMargin(10, 20, 10, 0)
|
||||
|
||||
-- Добавляем вкладки с разными стилями
|
||||
local tab1 = vgui.Create('DPanel')
|
||||
tab1.Paint = function(_, w, h)
|
||||
draw.SimpleText('Вкладка 1', 'Fated.20', w/2, h/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
tabs:AddTab('Вкладка 1', tab1, 'icon16/page_white.png')
|
||||
|
||||
local tab2 = vgui.Create('DPanel')
|
||||
tab2.Paint = function(_, w, h)
|
||||
draw.SimpleText('Вкладка 2', 'Fated.20', w/2, h/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
tabs:AddTab('Вкладка 2', tab2, 'icon16/page_white_edit.png', Color(100, 200, 100))
|
||||
|
||||
local tab3 = vgui.Create('DPanel')
|
||||
tab3.Paint = function(_, w, h)
|
||||
draw.SimpleText('Вкладка 3', 'Fated.20', w/2, h/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
tabs:AddTab('Вкладка 3', tab3, 'icon16/page_white_gear.png', nil, Color(200, 100, 100))
|
||||
|
||||
tabs:ActiveTab('Вкладка 1')
|
||||
end
|
||||
CreateCategory('Legacy Frame (не стоит использовать)', {
|
||||
{'Mantle.ui.frame(object frame, string title, int w, int h, bool cls_btn, bool open_anim)', 'Оформление стандартное окна стилем Mantle'},
|
||||
{'Mantle.ui.sp(object scroll)', 'Оформление панели прокрутки элементов'},
|
||||
{'Mantle.ui.btn(object btn, mat icon, int icon_size, color col, int rad, bool off_grad, color hov, bool off_hov)', 'Оформление кнопки'},
|
||||
{'Mantle.ui.slidebox(object parent, string label, int min_value, int max_value, string convar, int decimals)', 'Создание слайдера на родительном элементе'},
|
||||
{'Mantle.ui.desc_entry(object parent, string title, string placeholder, bool off_title)', 'Создание поля ввода'},
|
||||
{'Mantle.ui.checkbox(object parent, string text, string convar)', 'Создание чекбокса'},
|
||||
{'Mantle.ui.panel_tabs(object parent)', 'Создание панели с вкладками. В дальнейшем использовать :AddTab() и :ActiveTab() для настройки'}
|
||||
}, panel, btnFrame, true)
|
||||
|
||||
return panel
|
||||
end
|
||||
|
||||
tabs:AddTab('Legacy UI', CreateLegacyTest(), Material('icon16/exclamation.png'))
|
||||
|
||||
local function CreateSettings()
|
||||
local panel = vgui.Create('MantleScrollPanel')
|
||||
CreateTabHeader('Настройки', 'Глобальные настройки Mantle: темы, эффекты и глубины элементов.', Material('icon16/cog.png'), panel)
|
||||
|
||||
local menuWide = menuMantle:GetWide()
|
||||
|
||||
local checkboxDepth = vgui.Create('MantleCheckBox', panel)
|
||||
checkboxDepth:Dock(TOP)
|
||||
checkboxDepth:SetTxt('Глубины элементов')
|
||||
checkboxDepth:SetConvar('mantle_depth_ui')
|
||||
|
||||
local checkboxBlur = vgui.Create('MantleCheckBox', panel)
|
||||
checkboxBlur:Dock(TOP)
|
||||
checkboxBlur:DockMargin(0, 6, 0, 0)
|
||||
checkboxBlur:SetTxt('Размытие фона')
|
||||
checkboxBlur:SetConvar('mantle_blur')
|
||||
|
||||
local categoryTheme = vgui.Create('MantleCategory', panel)
|
||||
categoryTheme:Dock(TOP)
|
||||
categoryTheme:DockMargin(0, 6, 0, 0)
|
||||
categoryTheme:SetText('Изменение цветовой темы')
|
||||
categoryTheme:SetActive(true)
|
||||
|
||||
local comboboxTheme = vgui.Create('MantleComboBox')
|
||||
comboboxTheme:Dock(TOP)
|
||||
comboboxTheme:SetPlaceholder('Выберите тему интерфейса')
|
||||
comboboxTheme:AddChoice('Тёмная (dark)', 'dark')
|
||||
comboboxTheme:AddChoice('Тёмная монотонная (dark_mono)', 'dark_mono')
|
||||
comboboxTheme:AddChoice('Светлая (light)', 'light')
|
||||
comboboxTheme:AddChoice('Синяя (blue)', 'blue')
|
||||
comboboxTheme:AddChoice('Красная (red)', 'red')
|
||||
comboboxTheme:AddChoice('Зелёная (green)', 'green')
|
||||
comboboxTheme:AddChoice('Оранжевая (orange)', 'orange')
|
||||
comboboxTheme:AddChoice('Фиолетовый (purple)', 'purple')
|
||||
comboboxTheme:AddChoice('Кофейная (coffee)', 'coffee')
|
||||
comboboxTheme:AddChoice('Ледяная (ice)', 'ice')
|
||||
comboboxTheme:AddChoice('Винная (wine)', 'wine')
|
||||
comboboxTheme:AddChoice('Фиалковая (violet)', 'violet')
|
||||
comboboxTheme:AddChoice('Моховая (moss)', 'moss')
|
||||
comboboxTheme:AddChoice('Коралловая (coral)', 'coral')
|
||||
comboboxTheme.OnSelect = function(_, _, data)
|
||||
RunConsoleCommand('mantle_theme', data)
|
||||
end
|
||||
categoryTheme:AddItem(comboboxTheme)
|
||||
|
||||
local listThemeColors = vgui.Create('DIconLayout')
|
||||
listThemeColors:Dock(TOP)
|
||||
listThemeColors:DockMargin(6, 8, 6, 0)
|
||||
listThemeColors:SetTall(164)
|
||||
listThemeColors:SetSpaceX(8)
|
||||
listThemeColors:SetSpaceY(8)
|
||||
categoryTheme:AddItem(listThemeColors)
|
||||
|
||||
for colId, _ in pairs(Mantle.color) do
|
||||
local panCol = vgui.Create('DPanel', listThemeColors)
|
||||
panCol:SetSize(80, 80)
|
||||
panCol.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color[colId])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
draw.SimpleText(colId, 'Fated.12', w * 0.5, h * 0.5, color_black, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
return panel
|
||||
end
|
||||
|
||||
tabs:AddTab('Настройки', CreateSettings(), Material('icon16/cog.png'))
|
||||
end
|
||||
|
||||
concommand.Add('mantle_menu', CreateMenu)
|
||||
106
addons/mantle_darkfated_ultracode/lua/mantle/core/vgui.lua
Normal file
106
addons/mantle_darkfated_ultracode/lua/mantle/core/vgui.lua
Normal file
@@ -0,0 +1,106 @@
|
||||
CreateClientConVar('mantle_depth_ui', 1, true, false)
|
||||
CreateClientConVar('mantle_theme', 'dark', true, false)
|
||||
CreateClientConVar('mantle_blur', 1, true, false)
|
||||
|
||||
Mantle.ui = {
|
||||
convar = {
|
||||
depth_ui = GetConVar('mantle_depth_ui'):GetBool(),
|
||||
theme = GetConVar('mantle_theme'):GetString(),
|
||||
blur = GetConVar('mantle_blur'):GetBool()
|
||||
}
|
||||
}
|
||||
|
||||
local themeMap = {
|
||||
dark = Mantle.color_dark,
|
||||
dark_mono = Mantle.color_dark_mono,
|
||||
graphite = Mantle.color_graphite,
|
||||
light = Mantle.color_light,
|
||||
blue = Mantle.color_blue,
|
||||
red = Mantle.color_red,
|
||||
green = Mantle.color_green,
|
||||
orange = Mantle.color_orange,
|
||||
purple = Mantle.color_purple,
|
||||
coffee = Mantle.color_coffee,
|
||||
ice = Mantle.color_ice,
|
||||
wine = Mantle.color_wine,
|
||||
violet = Mantle.color_violet,
|
||||
moss = Mantle.color_moss,
|
||||
coral = Mantle.color_coral
|
||||
}
|
||||
|
||||
local function isColor(v)
|
||||
return type(v) == 'table' and type(v.r) == 'number'
|
||||
end
|
||||
|
||||
local transition = {
|
||||
active = false,
|
||||
to = nil,
|
||||
progress = 0,
|
||||
speed = 3,
|
||||
colorBlend = 8
|
||||
}
|
||||
|
||||
local function startThemeTransition(name)
|
||||
transition.to = table.Copy(themeMap[name] or Mantle.color_dark)
|
||||
transition.active = true
|
||||
transition.progress = 0
|
||||
|
||||
if !hook.GetTable().MantleThemeTransition then
|
||||
hook.Add('Think', 'MantleThemeTransition', function()
|
||||
if !transition.active then return end
|
||||
|
||||
local dt = FrameTime()
|
||||
transition.progress = Mantle.func.approachExp(transition.progress, 1, transition.speed, dt)
|
||||
local eased = Mantle.func.easeOutCubic(transition.progress)
|
||||
|
||||
local to = transition.to
|
||||
if !to then
|
||||
transition.active = false
|
||||
hook.Remove('Think', 'MantleThemeTransition')
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(to) do
|
||||
if isColor(v) then
|
||||
Mantle.color[k] = Mantle.func.LerpColor(transition.colorBlend, Mantle.color[k] or v, v)
|
||||
elseif type(v) == 'table' and #v > 0 then
|
||||
Mantle.color[k] = Mantle.color[k] or {}
|
||||
for i = 1, #v do
|
||||
local vi = v[i]
|
||||
if isColor(vi) then
|
||||
Mantle.color[k][i] = Mantle.func.LerpColor(transition.colorBlend, (Mantle.color[k] and Mantle.color[k][i]) or vi, vi)
|
||||
else
|
||||
Mantle.color[k][i] = vi
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if transition.progress >= 0.999 then
|
||||
Mantle.color = table.Copy(transition.to)
|
||||
transition.active = false
|
||||
hook.Remove('Think', 'MantleThemeTransition')
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local function ApplyInitialTheme()
|
||||
local theme = Mantle.ui.convar.theme
|
||||
Mantle.color = table.Copy(themeMap[theme] or Mantle.color_dark)
|
||||
end
|
||||
|
||||
ApplyInitialTheme()
|
||||
|
||||
cvars.AddChangeCallback('mantle_depth_ui', function(_, _, newValue)
|
||||
Mantle.ui.convar.depth_ui = newValue == '1'
|
||||
end)
|
||||
|
||||
cvars.AddChangeCallback('mantle_theme', function(_, _, newValue)
|
||||
Mantle.ui.convar.theme = newValue
|
||||
startThemeTransition(newValue)
|
||||
end)
|
||||
|
||||
cvars.AddChangeCallback('mantle_blur', function(_, _, newValue)
|
||||
Mantle.ui.convar.blur = newValue == '1'
|
||||
end)
|
||||
@@ -0,0 +1,191 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self._activeShadowTimer = 0
|
||||
self._activeShadowMinTime = 0.03 -- минимальная длительность (сек)
|
||||
self._activeShadowLerp = 0
|
||||
self.hover_status = 0
|
||||
self.bool_hover = true
|
||||
self.font = 'Fated.18'
|
||||
self.radius = 16
|
||||
self.icon = ''
|
||||
self.icon_size = 16
|
||||
self.text = Mantle.lang.get('mantle', 'btn_default')
|
||||
self.col = Mantle.color.button
|
||||
self.col_hov = Mantle.color.button_hovered
|
||||
self.bool_gradient = true
|
||||
self.click_alpha = 0
|
||||
self.click_x = 0
|
||||
self.click_y = 0
|
||||
self.ripple_speed = 4
|
||||
self.enable_ripple = false
|
||||
self.ripple_color = Color(255, 255, 255, 30)
|
||||
|
||||
--[[
|
||||
TODO: тень, которая не вылезает за окно при прокрутке
|
||||
]]--
|
||||
-- local parent = self:GetParent()
|
||||
-- local grandParent = IsValid(parent:GetParent()) and parent:GetParent() or parent
|
||||
-- self.clipParent = IsValid(parent) and grandParent or nil
|
||||
|
||||
self:SetText('')
|
||||
end
|
||||
|
||||
function PANEL:SetHover(is_hover)
|
||||
self.bool_hover = is_hover
|
||||
end
|
||||
|
||||
function PANEL:SetFont(font)
|
||||
self.font = font
|
||||
end
|
||||
|
||||
function PANEL:SetRadius(rad)
|
||||
self.radius = rad
|
||||
end
|
||||
|
||||
function PANEL:SetIcon(icon, icon_size)
|
||||
self.icon = type(icon) == 'IMaterial' and icon or Material(icon)
|
||||
self.icon_size = icon_size
|
||||
end
|
||||
|
||||
function PANEL:SetTxt(text)
|
||||
self.text = text
|
||||
end
|
||||
|
||||
function PANEL:SetColor(col)
|
||||
self.col = col
|
||||
end
|
||||
|
||||
function PANEL:SetColorHover(col)
|
||||
self.col_hov = col
|
||||
end
|
||||
|
||||
function PANEL:SetGradient(is_grad)
|
||||
self.bool_gradient = is_grad
|
||||
end
|
||||
|
||||
function PANEL:SetRipple(enable)
|
||||
self.enable_ripple = enable
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(mousecode)
|
||||
self.BaseClass.OnMousePressed(self, mousecode)
|
||||
|
||||
if self.enable_ripple and mousecode == MOUSE_LEFT then
|
||||
self.click_alpha = 1
|
||||
self.click_x, self.click_y = self:CursorPos()
|
||||
end
|
||||
end
|
||||
|
||||
local math_clamp = math.Clamp
|
||||
local btnFlags = RNDX.SHAPE_IOS
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
if self:IsHovered() then
|
||||
self.hover_status = math_clamp(self.hover_status + 4 * FrameTime(), 0, 1)
|
||||
else
|
||||
self.hover_status = math_clamp(self.hover_status - 8 * FrameTime(), 0, 1)
|
||||
end
|
||||
|
||||
-- Минимальный порог длительности для активной тени
|
||||
local isActive = (self:IsDown() or self.Depressed) and self.hover_status > 0.8
|
||||
if isActive then
|
||||
self._activeShadowTimer = SysTime() + self._activeShadowMinTime
|
||||
end
|
||||
local showActiveShadow = isActive or (self._activeShadowTimer > SysTime())
|
||||
|
||||
-- Плавная анимация дополнительной тени при зажатии
|
||||
local activeTarget = showActiveShadow and 10 or 0
|
||||
local activeSpeed = (activeTarget > 0) and 7 or 3 -- скорость появления/затухания
|
||||
self._activeShadowLerp = Lerp(FrameTime() * activeSpeed, self._activeShadowLerp, activeTarget)
|
||||
|
||||
-- Обычная тень
|
||||
-- if Mantle.ui.convar.depth_ui then
|
||||
-- RNDX().Rect(0, 0, w, h)
|
||||
-- :Rad(self.radius)
|
||||
-- :Color(Mantle.color.window_shadow)
|
||||
-- :Shape(RNDX.SHAPE_IOS)
|
||||
-- :Shadow(5, 20)
|
||||
-- :Clip(self.clipParent)
|
||||
-- :Draw()
|
||||
-- end
|
||||
|
||||
-- Дополнительная тень при зажатии
|
||||
if self._activeShadowLerp > 0 and Mantle.ui.convar.depth_ui then
|
||||
local col = Color(self.col_hov.r, self.col_hov.g, self.col_hov.b, math.Clamp(self.col_hov.a * 1.5, 0, 255))
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(self.radius)
|
||||
:Color(col)
|
||||
:Shape(btnFlags)
|
||||
:Shadow(self._activeShadowLerp * 1.5, 24)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(self.radius)
|
||||
:Color(self.col)
|
||||
:Shape(btnFlags)
|
||||
:Draw()
|
||||
|
||||
if self.bool_gradient then
|
||||
Mantle.func.gradient(0, 0, w, h, 1, Mantle.color.button_shadow, self.radius, btnFlags)
|
||||
end
|
||||
|
||||
if self.bool_hover then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(self.radius)
|
||||
:Color(Color(self.col_hov.r, self.col_hov.g, self.col_hov.b, self.hover_status * 255))
|
||||
:Shape(btnFlags)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
if self.click_alpha > 0 then
|
||||
self.click_alpha = math_clamp(self.click_alpha - FrameTime() * self.ripple_speed, 0, 1)
|
||||
|
||||
local ripple_size = (1 - self.click_alpha) * math.max(w, h) * 2
|
||||
local ripple_color = Color(
|
||||
self.ripple_color.r,
|
||||
self.ripple_color.g,
|
||||
self.ripple_color.b,
|
||||
self.ripple_color.a * self.click_alpha
|
||||
)
|
||||
|
||||
RNDX().Rect(self.click_x - ripple_size * 0.5, self.click_y - ripple_size * 0.5, ripple_size, ripple_size)
|
||||
:Rad(100)
|
||||
:Color(ripple_color)
|
||||
:Shape(btnFlags)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
if self.text != '' then
|
||||
draw.SimpleText(
|
||||
self.text,
|
||||
self.font,
|
||||
w * 0.5 + (self.icon ~= '' and self.icon_size * 0.5 + 2 or 0),
|
||||
h * 0.5,
|
||||
Mantle.color.text,
|
||||
TEXT_ALIGN_CENTER,
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
if self.icon != '' then
|
||||
surface.SetFont(self.font)
|
||||
local posX = (w - surface.GetTextSize(self.text) - self.icon_size) * 0.5 - 2
|
||||
local posY = (h - self.icon_size) * 0.5
|
||||
RNDX().Rect(posX, posY, self.icon_size, self.icon_size)
|
||||
:Material(self.icon)
|
||||
:Color(color_white)
|
||||
:Shape(btnFlags)
|
||||
:Draw()
|
||||
end
|
||||
elseif self.icon != '' then
|
||||
local posX = (w - self.icon_size) * 0.5
|
||||
local posY = (h - self.icon_size) * 0.5
|
||||
RNDX().Rect(posX, posY, self.icon_size, self.icon_size)
|
||||
:Material(self.icon)
|
||||
:Color(color_white)
|
||||
:Shape(btnFlags)
|
||||
:Draw()
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register('MantleBtn', PANEL, 'Button')
|
||||
@@ -0,0 +1,130 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetTall(30)
|
||||
self:DockPadding(0, 36, 0, 0)
|
||||
self.name = 'Категория'
|
||||
self.bool_opened = false
|
||||
self.bool_header_centered = false
|
||||
self.content_size = 0
|
||||
self.header_color = Mantle.color.category
|
||||
self.header_color_standard = self.header_color
|
||||
self.header_color_opened = Mantle.color.category_opened
|
||||
|
||||
self._childHeights = {}
|
||||
|
||||
self._anim = 0
|
||||
self._animTarget = 0
|
||||
self._animSpeed = 12
|
||||
self._animEased = 0
|
||||
|
||||
self.header = vgui.Create('Button', self)
|
||||
self.header:SetText('')
|
||||
self.header.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(self.header_color)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
local posX = self.bool_header_centered and w * 0.5 or 8
|
||||
local alignX = self.bool_header_centered and TEXT_ALIGN_CENTER or TEXT_ALIGN_LEFT
|
||||
draw.SimpleText(self.name, 'Fated.20', posX, 4, Mantle.color.text, alignX)
|
||||
|
||||
self.header_color = Mantle.func.LerpColor(8, self.header_color, self.bool_opened and self.header_color_opened or self.header_color_standard)
|
||||
end
|
||||
self.header.DoClick = function()
|
||||
self.bool_opened = !self.bool_opened
|
||||
self._animTarget = self.bool_opened and 1 or 0
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetText(name)
|
||||
self.name = name
|
||||
end
|
||||
|
||||
function PANEL:SetCenterText(is_centered)
|
||||
self.bool_header_centered = is_centered
|
||||
end
|
||||
|
||||
local function getTopBottomMargin(pnl)
|
||||
if !pnl.GetDockMargin then return 0, 0 end
|
||||
local ok, l, t, r, b = pcall(function()
|
||||
return pnl:GetDockMargin()
|
||||
end)
|
||||
if !ok or !l then return 0, 0 end
|
||||
return t or 0, b or 0
|
||||
end
|
||||
|
||||
function PANEL:AddItem(panel)
|
||||
panel:SetParent(self)
|
||||
|
||||
local top, bottom = getTopBottomMargin(panel)
|
||||
local contribution = (panel.GetTall and panel:GetTall() or 0) + top + bottom
|
||||
|
||||
self._childHeights[panel] = contribution
|
||||
self.content_size = (self.content_size or 0) + contribution
|
||||
|
||||
if self.bool_opened then
|
||||
self:SetTall(30 + self.content_size + 12)
|
||||
end
|
||||
|
||||
local old = panel.OnSizeChanged
|
||||
panel.OnSizeChanged = function(...)
|
||||
if old then pcall(old, ...) end
|
||||
if !IsValid(self) then return end
|
||||
|
||||
local nt, nb = getTopBottomMargin(panel)
|
||||
local newContribution = (panel.GetTall and panel:GetTall() or 0) + nt + nb
|
||||
local oldContribution = self._childHeights[panel] or 0
|
||||
local delta = newContribution - oldContribution
|
||||
if delta != 0 then
|
||||
self._childHeights[panel] = newContribution
|
||||
self.content_size = math.max(0, (self.content_size or 0) + delta)
|
||||
end
|
||||
end
|
||||
|
||||
return panel
|
||||
end
|
||||
|
||||
function PANEL:SetColor(col)
|
||||
self.header_color_standard = col
|
||||
if !self.bool_opened then
|
||||
self.header_color = self.header_color_standard
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetActive(is_active)
|
||||
is_active = tobool(is_active)
|
||||
if self.bool_opened == is_active then return end
|
||||
self.bool_opened = is_active
|
||||
self._animTarget = is_active and 1 or 0
|
||||
self.header_color = is_active and self.header_color_opened or self.header_color_standard
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
self.header:SetSize(w, 30)
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
local ft = FrameTime()
|
||||
|
||||
self._anim = Mantle.func.approachExp(self._anim, self._animTarget, self._animSpeed, ft)
|
||||
self._animEased = Mantle.func.easeOutCubic(self._anim)
|
||||
|
||||
local currentContentTall = (self.content_size or 0) * self._animEased
|
||||
|
||||
local padded = 12 * self._animEased
|
||||
|
||||
local totalTall = 30 + currentContentTall + padded
|
||||
self:SetTall(math.max(30, math.floor(totalTall + 0.5)))
|
||||
|
||||
local alphaVal = math.floor(255 * self._animEased + 0.5)
|
||||
|
||||
for _, c in ipairs(self:GetChildren()) do
|
||||
if IsValid(c) and c != self.header then
|
||||
if c.SetAlpha then c:SetAlpha(alphaVal) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register('MantleCategory', PANEL, 'Panel')
|
||||
@@ -0,0 +1,136 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.text = ''
|
||||
self.convar = ''
|
||||
self.value = false
|
||||
|
||||
self:SetText('')
|
||||
self:SetCursor('hand')
|
||||
self:SetTall(36)
|
||||
|
||||
self._circle = 0
|
||||
self._circleEased = 0
|
||||
self._circleColor = table.Copy(Mantle.color.gray)
|
||||
|
||||
self.toggle = vgui.Create('Button', self)
|
||||
self.toggle:Dock(RIGHT)
|
||||
self.toggle:SetWide(48)
|
||||
self.toggle:DockMargin(0, 0, 14, 0)
|
||||
self.toggle:SetText('')
|
||||
self.toggle:SetCursor('hand')
|
||||
self.toggle.Paint = nil
|
||||
|
||||
self.toggle.DoClick = function()
|
||||
if self.convar ~= '' then
|
||||
LocalPlayer():ConCommand(self.convar .. ' ' .. (self.value and 0 or 1))
|
||||
end
|
||||
|
||||
self:SetValue(not self.value)
|
||||
self:OnChange(self.value)
|
||||
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
self.toggle:DoClick()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetTxt(text)
|
||||
self.text = text
|
||||
end
|
||||
|
||||
function PANEL:SetValue(val)
|
||||
self.value = tobool(val)
|
||||
end
|
||||
|
||||
function PANEL:GetBool()
|
||||
return self.value
|
||||
end
|
||||
|
||||
function PANEL:SetConvar(convar)
|
||||
local c = GetConVar(convar)
|
||||
if c then self.value = c:GetBool() end
|
||||
self.convar = convar
|
||||
end
|
||||
|
||||
function PANEL:OnChange(new_value)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
if Mantle.ui and Mantle.ui.convar and Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(12)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(6, 22)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(12)
|
||||
:Color(Mantle.color.focus_panel)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
|
||||
local textX = 14
|
||||
draw.SimpleText(self.text, 'Fated.18', textX, h * 0.5, Mantle.color.text, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
function PANEL:PaintOver(w, h)
|
||||
local tw, th = self.toggle:GetWide(), self.toggle:GetTall()
|
||||
local tx, ty = self.toggle:GetPos()
|
||||
local ft = FrameTime()
|
||||
|
||||
local target = self.value and 1 or 0
|
||||
local circleSpeed = 8
|
||||
self._circle = Mantle.func.approachExp(self._circle, target, circleSpeed, ft)
|
||||
if math.abs(self._circle - target) < 0.001 then self._circle = target end
|
||||
self._circleEased = Mantle.func.easeInOutCubic(self._circle)
|
||||
|
||||
local trackW = tw - 10
|
||||
local trackH = 18
|
||||
local trackX = tx + (tw - trackW) / 2
|
||||
local trackY = ty + (th - trackH) / 2
|
||||
|
||||
RNDX().Rect(trackX, trackY + 1, trackW, trackH - 2)
|
||||
:Rad(trackH / 2)
|
||||
:Color(Mantle.color.toggle)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
|
||||
local circleSize = 20
|
||||
local pad = 0
|
||||
local textMargin = 14
|
||||
|
||||
local x0_base = trackX + pad - (circleSize * 0.5) + 0.5
|
||||
local x1 = trackX + trackW - pad - (circleSize * 0.5) - 0.5
|
||||
local x0_align = textMargin - (circleSize * 0.5)
|
||||
local x0 = math.max(x0_base, x0_align)
|
||||
|
||||
local circleXPrec = x0 + (x1 - x0) * self._circleEased
|
||||
local circleCenterX = circleXPrec + circleSize * 0.5
|
||||
local circleCenterY = trackY + trackH * 0.5
|
||||
|
||||
local baseCircle = self.value and Mantle.color.theme or Mantle.color.gray
|
||||
local circleCol = table.Copy(baseCircle)
|
||||
circleCol.a = 255
|
||||
self._circleColor = Mantle.func.LerpColor(12, self._circleColor, circleCol)
|
||||
RNDX().Circle(circleCenterX, circleCenterY, circleSize)
|
||||
:Color(self._circleColor)
|
||||
:Draw()
|
||||
|
||||
RNDX().Circle(circleCenterX, circleCenterY + 2, circleSize * 1.05)
|
||||
:Color(Color(0, 0, 0, 30))
|
||||
:Draw()
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
self.toggle:SetWide(48)
|
||||
self.toggle:DockMargin(0, 0, 14, 0)
|
||||
end
|
||||
|
||||
vgui.Register('MantleCheckBox', PANEL, 'Panel')
|
||||
@@ -0,0 +1,222 @@
|
||||
local color_close = Color(210, 65, 65)
|
||||
local color_accept = Color(44, 124, 62)
|
||||
local color_outline = Color(30, 30, 30)
|
||||
local color_target = Color(255, 255, 255, 200)
|
||||
|
||||
function Mantle.ui.color_picker(func, color_standart)
|
||||
if IsValid(Mantle.ui.menu_color_picker) then
|
||||
Mantle.ui.menu_color_picker:Remove()
|
||||
end
|
||||
|
||||
local selected_color = color_standart or Color(255, 255, 255)
|
||||
local hue = 0
|
||||
local saturation = 1
|
||||
local value = 1
|
||||
|
||||
if color_standart then
|
||||
local r, g, b = color_standart.r / 255, color_standart.g / 255, color_standart.b / 255
|
||||
local h, s, v = ColorToHSV(Color(r * 255, g * 255, b * 255))
|
||||
hue = h
|
||||
saturation = s
|
||||
value = v
|
||||
end
|
||||
|
||||
Mantle.ui.menu_color_picker = vgui.Create('MantleFrame')
|
||||
Mantle.ui.menu_color_picker:SetSize(300, 378)
|
||||
Mantle.ui.menu_color_picker:Center()
|
||||
Mantle.ui.menu_color_picker:MakePopup()
|
||||
Mantle.ui.menu_color_picker:SetTitle('')
|
||||
Mantle.ui.menu_color_picker:SetCenterTitle(Mantle.lang.get('mantle', 'color_title'))
|
||||
|
||||
local container = vgui.Create('Panel', Mantle.ui.menu_color_picker)
|
||||
container:Dock(FILL)
|
||||
container:DockMargin(10, 10, 10, 10)
|
||||
container.Paint = nil
|
||||
|
||||
local preview = vgui.Create('Panel', container)
|
||||
preview:Dock(TOP)
|
||||
preview:SetTall(40)
|
||||
preview:DockMargin(0, 0, 0, 10)
|
||||
preview.Paint = function(self, w, h)
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(2, 2, w - 4, h - 4)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(5, 20)
|
||||
:Draw()
|
||||
end
|
||||
RNDX.Draw(16, 2, 2, w - 4, h - 4, selected_color, RNDX.SHAPE_IOS)
|
||||
end
|
||||
|
||||
local colorField = vgui.Create('Panel', container)
|
||||
colorField:Dock(TOP)
|
||||
colorField:SetTall(200)
|
||||
colorField:DockMargin(0, 0, 0, 10)
|
||||
|
||||
local colorCursor = { x = 0, y = 0 }
|
||||
local isDraggingColor = false
|
||||
|
||||
colorField.OnMousePressed = function(self, keyCode)
|
||||
if keyCode == MOUSE_LEFT then
|
||||
isDraggingColor = true
|
||||
self:OnCursorMoved(self:CursorPos())
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
|
||||
colorField.OnMouseReleased = function(self, keyCode)
|
||||
if keyCode == MOUSE_LEFT then
|
||||
isDraggingColor = false
|
||||
end
|
||||
end
|
||||
|
||||
colorField.OnCursorMoved = function(self, x, y)
|
||||
if isDraggingColor then
|
||||
local w, h = self:GetSize()
|
||||
x = math.Clamp(x, 0, w)
|
||||
y = math.Clamp(y, 0, h)
|
||||
|
||||
colorCursor.x = x
|
||||
colorCursor.y = y
|
||||
|
||||
saturation = x / w
|
||||
value = 1 - (y / h)
|
||||
|
||||
selected_color = HSVToColor(hue, saturation, value)
|
||||
end
|
||||
end
|
||||
|
||||
colorField.Paint = function(self, w, h)
|
||||
local segments = 80
|
||||
local segmentSize = w / segments
|
||||
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(5, 20)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
for x = 0, segments do
|
||||
for y = 0, segments do
|
||||
local s = x / segments
|
||||
local v = 1 - (y / segments)
|
||||
local segX = x * segmentSize
|
||||
local segY = y * segmentSize
|
||||
|
||||
surface.SetDrawColor(HSVToColor(hue, s, v))
|
||||
surface.DrawRect(segX, segY, segmentSize + 1, segmentSize + 1)
|
||||
end
|
||||
end
|
||||
|
||||
RNDX().Circle(colorCursor.x, colorCursor.y, 12)
|
||||
:Outline(2)
|
||||
:Color(color_target)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local hueSlider = vgui.Create('Panel', container)
|
||||
hueSlider:Dock(TOP)
|
||||
hueSlider:SetTall(20)
|
||||
hueSlider:DockMargin(0, 0, 0, 10)
|
||||
|
||||
local huePos = 0
|
||||
local isDraggingHue = false
|
||||
|
||||
hueSlider.OnMousePressed = function(self, keyCode)
|
||||
if keyCode == MOUSE_LEFT then
|
||||
isDraggingHue = true
|
||||
self:OnCursorMoved(self:CursorPos())
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
|
||||
hueSlider.OnMouseReleased = function(self, keyCode)
|
||||
if keyCode == MOUSE_LEFT then
|
||||
isDraggingHue = false
|
||||
end
|
||||
end
|
||||
|
||||
hueSlider.OnCursorMoved = function(self, x, y)
|
||||
if isDraggingHue then
|
||||
local w = self:GetWide()
|
||||
x = math.Clamp(x, 0, w)
|
||||
|
||||
huePos = x
|
||||
hue = (x / w) * 360
|
||||
|
||||
selected_color = HSVToColor(hue, saturation, value)
|
||||
end
|
||||
end
|
||||
|
||||
hueSlider.Paint = function(self, w, h)
|
||||
local segments = 100
|
||||
local segmentWidth = w / segments
|
||||
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(5, 20)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
for i = 0, segments - 1 do
|
||||
local hueVal = (i / segments) * 360
|
||||
local x = i * segmentWidth
|
||||
|
||||
surface.SetDrawColor(HSVToColor(hueVal, 1, 1))
|
||||
surface.DrawRect(x, 1, segmentWidth + 1, h - 2)
|
||||
end
|
||||
|
||||
RNDX().Rect(huePos - 2, 0, 4, h)
|
||||
:Color(color_target)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local rgbContainer = vgui.Create('Panel', container)
|
||||
rgbContainer:Dock(TOP)
|
||||
rgbContainer:SetTall(60)
|
||||
rgbContainer:DockMargin(0, 0, 0, 10)
|
||||
rgbContainer.Paint = nil
|
||||
|
||||
local btnContainer = vgui.Create('Panel', container)
|
||||
btnContainer:Dock(BOTTOM)
|
||||
btnContainer:SetTall(30)
|
||||
btnContainer.Paint = nil
|
||||
|
||||
local btnClose = vgui.Create('MantleBtn', btnContainer)
|
||||
btnClose:Dock(LEFT)
|
||||
btnClose:SetWide(90)
|
||||
btnClose:SetTxt(Mantle.lang.get('mantle', 'color_cancel'))
|
||||
btnClose:SetColorHover(color_close)
|
||||
btnClose.DoClick = function()
|
||||
Mantle.ui.menu_color_picker:Remove()
|
||||
Mantle.func.sound()
|
||||
end
|
||||
|
||||
local btnSelect = vgui.Create('MantleBtn', btnContainer)
|
||||
btnSelect:Dock(RIGHT)
|
||||
btnSelect:SetWide(90)
|
||||
btnSelect:SetTxt(Mantle.lang.get('mantle', 'color_select'))
|
||||
btnSelect:SetColorHover(color_accept)
|
||||
btnSelect.DoClick = function()
|
||||
Mantle.func.sound()
|
||||
func(selected_color)
|
||||
Mantle.ui.menu_color_picker:Remove()
|
||||
end
|
||||
|
||||
timer.Simple(0, function()
|
||||
if IsValid(colorField) and IsValid(hueSlider) then
|
||||
colorCursor.x = saturation * colorField:GetWide()
|
||||
colorCursor.y = (1 - value) * colorField:GetTall()
|
||||
huePos = (hue / 360) * hueSlider:GetWide()
|
||||
end
|
||||
end)
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
Mantle.ui.menu_color_picker:SetAlpha(255)
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,267 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.choices = {}
|
||||
self.selected = nil
|
||||
self.opened = false
|
||||
self:SetTall(26)
|
||||
self:SetText('')
|
||||
self.font = 'Fated.18'
|
||||
self.hoverAnim = 0
|
||||
self.OnSelect = function(_, _, _) end
|
||||
|
||||
self.btn = vgui.Create('DButton', self)
|
||||
self.btn:Dock(FILL)
|
||||
self.btn:SetText('')
|
||||
self.btn:SetCursor('hand')
|
||||
self.btn.Paint = function(_, w, h)
|
||||
if self.btn:IsHovered() then
|
||||
self.hoverAnim = math.Clamp(self.hoverAnim + FrameTime() * 4, 0, 1)
|
||||
else
|
||||
self.hoverAnim = math.Clamp(self.hoverAnim - FrameTime() * 8, 0, 1)
|
||||
end
|
||||
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(5, 20)
|
||||
:Draw()
|
||||
end
|
||||
RNDX.Draw(16, 0, 0, w, h, Mantle.color.focus_panel, RNDX.SHAPE_IOS)
|
||||
if self.hoverAnim > 0 then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Color(Mantle.color.button_hovered.r, Mantle.color.button_hovered.g, Mantle.color.button_hovered.b, self.hoverAnim * 255))
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
draw.SimpleText(
|
||||
self.selected or self.placeholder or 'Выберите...',
|
||||
self.font,
|
||||
12,
|
||||
h * 0.5,
|
||||
Mantle.color.text,
|
||||
TEXT_ALIGN_LEFT,
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
|
||||
local arrowSize = 6
|
||||
local arrowX = w - 16
|
||||
local arrowY = h / 2
|
||||
local arrowColor = ColorAlpha(Mantle.color.text, 180 + self.hoverAnim * 75)
|
||||
|
||||
surface.SetDrawColor(arrowColor)
|
||||
draw.NoTexture()
|
||||
if !self.opened then
|
||||
surface.DrawPoly({
|
||||
{x = arrowX - arrowSize, y = arrowY - arrowSize/2},
|
||||
{x = arrowX + arrowSize, y = arrowY - arrowSize/2},
|
||||
{x = arrowX, y = arrowY + arrowSize/2}
|
||||
})
|
||||
end
|
||||
end
|
||||
self.btn.DoClick = function()
|
||||
if self.opened then
|
||||
self:CloseMenu()
|
||||
else
|
||||
self:OpenMenu()
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:AddChoice(text, data)
|
||||
table.insert(self.choices, {text = text, data = data})
|
||||
end
|
||||
|
||||
function PANEL:SetValue(val)
|
||||
self.selected = val
|
||||
end
|
||||
|
||||
function PANEL:GetValue()
|
||||
return self.selected
|
||||
end
|
||||
|
||||
function PANEL:SetPlaceholder(text)
|
||||
self.placeholder = text
|
||||
end
|
||||
|
||||
function PANEL:OpenMenu()
|
||||
if IsValid(self.menu) then
|
||||
self.menu:Remove()
|
||||
end
|
||||
|
||||
local menuPadding = 6
|
||||
local itemHeight = 26
|
||||
local menuHeight = (#self.choices * (itemHeight + 2)) + (menuPadding * 2) + 2
|
||||
|
||||
local x, y = self:LocalToScreen(0, self:GetTall())
|
||||
|
||||
self.menu = vgui.Create('DPanel')
|
||||
self.menu:SetSize(self:GetWide(), menuHeight)
|
||||
if y + menuHeight > ScrH() - 10 then
|
||||
y = y - menuHeight - self:GetTall()
|
||||
end
|
||||
self.menu:SetPos(x, y)
|
||||
self.menu:SetDrawOnTop(true)
|
||||
self.menu:MakePopup()
|
||||
self.menu:SetKeyboardInputEnabled(false)
|
||||
self.menu:DockPadding(menuPadding, menuPadding, menuPadding, menuPadding)
|
||||
|
||||
self.menu._anim = 0
|
||||
self.menu._animTarget = 1
|
||||
self.menu._animSpeed = 18
|
||||
self.menu._animEased = 0
|
||||
self.menu._closing = false
|
||||
self.menu._disableBlur = false
|
||||
self.menu:SetAlpha(0)
|
||||
|
||||
self.menu.Paint = function(s, w, h)
|
||||
local aMul = s._animEased or ((s:GetAlpha() or 255) / 255)
|
||||
|
||||
local blurMul
|
||||
if s._closing or s._disableBlur or s._animTarget == 0 then
|
||||
blurMul = 0
|
||||
else
|
||||
local fadeStart = 0.3
|
||||
blurMul = math.Clamp((aMul - fadeStart) / (1 - fadeStart), 0, 1)
|
||||
end
|
||||
|
||||
local shadowSpread = math.max(0, math.floor(10 * blurMul))
|
||||
local shadowIntensity = math.max(0, math.floor(16 * blurMul))
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(shadowSpread, shadowIntensity)
|
||||
:Draw()
|
||||
|
||||
if not s._disableBlur then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Blur(blurMul)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.background_panelpopup)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.background_panelpopup)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Outline(1)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
surface.SetFont(self.font)
|
||||
for i, choice in ipairs(self.choices) do
|
||||
local option = vgui.Create('DButton', self.menu)
|
||||
option:SetText('')
|
||||
option:Dock(TOP)
|
||||
option:DockMargin(2, 2, 2, 0)
|
||||
option:SetTall(itemHeight)
|
||||
option:SetCursor('hand')
|
||||
option.Paint = function(s, w, h)
|
||||
if s:IsHovered() then
|
||||
RNDX.Draw(16, 0, 0, w, h, Mantle.color.hover, RNDX.SHAPE_IOS)
|
||||
end
|
||||
|
||||
draw.SimpleText(
|
||||
choice.text,
|
||||
'Fated.18',
|
||||
14,
|
||||
h * 0.5,
|
||||
Mantle.color.text,
|
||||
TEXT_ALIGN_LEFT,
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
|
||||
if self.selected == choice.text then
|
||||
RNDX.Draw(0, 4, h * 0.5 - 1, 4, 2, Mantle.color.theme)
|
||||
end
|
||||
end
|
||||
option.DoClick = function()
|
||||
self.selected = choice.text
|
||||
if self.menu and IsValid(self.menu) then
|
||||
self.menu._closing = true
|
||||
self.menu._animTarget = 0
|
||||
self.menu._disableBlur = true
|
||||
end
|
||||
|
||||
if self.OnSelect then
|
||||
self.OnSelect(i, choice.text, choice.data)
|
||||
end
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
|
||||
self.opened = true
|
||||
local oldMouseDown = false
|
||||
self.menu.Think = function(s)
|
||||
local ft = FrameTime()
|
||||
s._anim = Mantle.func.approachExp(s._anim or 0, s._animTarget or 1, s._animSpeed or 18, ft)
|
||||
s._animEased = s._anim
|
||||
s:SetAlpha(math.floor(255 * s._animEased + 0.5))
|
||||
|
||||
if s._targetX and s._targetY then
|
||||
local offsetY = 6 * (1 - s._animEased)
|
||||
s:SetPos(s._targetX, s._targetY + offsetY)
|
||||
end
|
||||
|
||||
if s._closing and s._animEased <= 0.005 then
|
||||
s:Remove()
|
||||
return
|
||||
end
|
||||
|
||||
if not s:IsVisible() then return end
|
||||
local mouseDown = input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT)
|
||||
if mouseDown and not oldMouseDown then
|
||||
local mx, my = gui.MousePos()
|
||||
local x, y = s:LocalToScreen(0, 0)
|
||||
if not (mx >= x and mx <= x + s:GetWide() and my >= y and my <= y + s:GetTall()) then
|
||||
s._closing = true
|
||||
s._animTarget = 0
|
||||
s._disableBlur = true
|
||||
end
|
||||
end
|
||||
oldMouseDown = mouseDown
|
||||
end
|
||||
|
||||
self.menu.OnRemove = function()
|
||||
if IsValid(self) then
|
||||
self.opened = false
|
||||
end
|
||||
end
|
||||
|
||||
Mantle.func.ClampMenuPosition(self.menu)
|
||||
self.menu._targetX, self.menu._targetY = self.menu:GetPos()
|
||||
if not self.menu._initPosSet then
|
||||
self.menu:SetPos(self.menu._targetX, self.menu._targetY + 6)
|
||||
self.menu._initPosSet = true
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CloseMenu()
|
||||
if IsValid(self.menu) then
|
||||
if not self.menu._closing then
|
||||
self.menu._closing = true
|
||||
self.menu._animTarget = 0
|
||||
self.menu._disableBlur = true
|
||||
end
|
||||
end
|
||||
self.opened = false
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
self:CloseMenu()
|
||||
end
|
||||
|
||||
vgui.Register('MantleComboBox', PANEL, 'Panel')
|
||||
@@ -0,0 +1,335 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.Items = {}
|
||||
self:SetSize(160, 0)
|
||||
self:DockPadding(4, 5, 4, 5)
|
||||
self:MakePopup()
|
||||
self:SetKeyboardInputEnabled(false)
|
||||
self:SetDrawOnTop(true)
|
||||
self.MaxTextWidth = 0
|
||||
|
||||
self._anim = 0
|
||||
self._animTarget = 1
|
||||
self._animSpeed = 18
|
||||
self._animEased = 0
|
||||
self._initPosSet = false
|
||||
self._closing = false
|
||||
self._disableBlur = false
|
||||
|
||||
self._openTime = CurTime()
|
||||
self:SetAlpha(0)
|
||||
|
||||
self.Think = function()
|
||||
local ft = FrameTime()
|
||||
|
||||
if not self._initPosSet then
|
||||
local tx, ty = self:GetPos()
|
||||
Mantle.func.ClampMenuPosition(self)
|
||||
self._targetX, self._targetY = self:GetPos()
|
||||
self:SetPos(self._targetX, self._targetY + 6)
|
||||
self._initPosSet = true
|
||||
end
|
||||
|
||||
if CurTime() - self._openTime >= 0.08 then
|
||||
if input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT) then
|
||||
if not self:IsChildHovered() then
|
||||
self:CloseMenu()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self._anim = Mantle.func.approachExp(self._anim, self._animTarget, self._animSpeed, ft)
|
||||
self._animEased = self._anim
|
||||
local a = math.floor(255 * self._animEased + 0.5)
|
||||
self:SetAlpha(a)
|
||||
|
||||
if self._targetX and self._targetY then
|
||||
local offsetY = 6 * (1 - self._animEased)
|
||||
self:SetPos(self._targetX, self._targetY + offsetY)
|
||||
end
|
||||
|
||||
if self._closing and self._animEased <= 0.005 then
|
||||
return self:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
local aMul = (self._animEased ~= nil) and self._animEased or ((self:GetAlpha() or 255) / 255)
|
||||
local blurMul
|
||||
|
||||
if self._closing or self._disableBlur or self._animTarget == 0 then
|
||||
blurMul = 0
|
||||
else
|
||||
local fadeStart = 0.3
|
||||
blurMul = math.Clamp((aMul - fadeStart) / (1 - fadeStart), 0, 1)
|
||||
end
|
||||
|
||||
local shadowSpread = math.max(0, math.floor(10 * blurMul))
|
||||
local shadowIntensity = math.max(0, math.floor(16 * blurMul))
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Color(Mantle.color.window_shadow.r, Mantle.color.window_shadow.g, Mantle.color.window_shadow.b, math.floor(100 * aMul)))
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(shadowSpread, shadowIntensity)
|
||||
:Draw()
|
||||
|
||||
if !self._disableBlur then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Blur(blurMul)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Color(Mantle.color.background_panelpopup.r, Mantle.color.background_panelpopup.g, Mantle.color.background_panelpopup.b, math.floor(150 * aMul)))
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Color(Mantle.color.background_panelpopup.r, Mantle.color.background_panelpopup.g, Mantle.color.background_panelpopup.b, math.floor(150 * aMul)))
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Outline(1)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
function PANEL:AddOption(text, func, icon, optData)
|
||||
surface.SetFont('Fated.18')
|
||||
local textW = select(1, surface.GetTextSize(text))
|
||||
self.MaxTextWidth = math.max(self.MaxTextWidth or 0, textW)
|
||||
|
||||
local option = vgui.Create('DButton', self)
|
||||
option:SetText('')
|
||||
option:Dock(TOP)
|
||||
option:DockMargin(2, 2, 2, 0)
|
||||
option:SetTall(26)
|
||||
option.sumTall = 28
|
||||
option.Icon = icon
|
||||
option.Text = text
|
||||
|
||||
option._submenu = nil
|
||||
option._submenu_open = false
|
||||
|
||||
option.DoClick = function()
|
||||
if option._submenu then
|
||||
if option._submenu_open then
|
||||
option:CloseSubMenu()
|
||||
else
|
||||
option:OpenSubMenu()
|
||||
end
|
||||
return
|
||||
end
|
||||
if func then func() end
|
||||
Mantle.func.sound()
|
||||
local function closeAllMenus(panel)
|
||||
while IsValid(panel) do
|
||||
if panel.GetName and panel:GetName() == 'MantleDermaMenu' then
|
||||
local parent = panel:GetParent()
|
||||
panel:CloseMenu()
|
||||
panel = parent
|
||||
else
|
||||
panel = panel:GetParent()
|
||||
end
|
||||
end
|
||||
end
|
||||
closeAllMenus(option)
|
||||
end
|
||||
|
||||
function option:AddSubMenu()
|
||||
if IsValid(option._submenu) then option._submenu:Remove() end
|
||||
local submenu = vgui.Create('MantleDermaMenu')
|
||||
submenu:SetDrawOnTop(true)
|
||||
submenu:SetParent(self:GetParent())
|
||||
submenu:SetVisible(false)
|
||||
option._submenu = submenu
|
||||
option._submenu_open = false
|
||||
|
||||
option.OnRemove = function()
|
||||
if IsValid(submenu) then submenu:Remove() end
|
||||
end
|
||||
|
||||
function option:OpenSubMenu()
|
||||
if not IsValid(submenu) then return end
|
||||
for _, sibling in ipairs(self:GetParent().Items or {}) do
|
||||
if sibling ~= self and sibling.CloseSubMenu then sibling:CloseSubMenu() end
|
||||
end
|
||||
local x, y = self:LocalToScreen(self:GetWide(), 0)
|
||||
submenu:SetPos(x, y)
|
||||
Mantle.func.ClampMenuPosition(submenu)
|
||||
submenu._targetX, submenu._targetY = submenu:GetPos()
|
||||
submenu:SetVisible(true)
|
||||
submenu:MakePopup()
|
||||
submenu:SetKeyboardInputEnabled(false)
|
||||
option._submenu_open = true
|
||||
end
|
||||
|
||||
function option:CloseSubMenu()
|
||||
if IsValid(submenu) then submenu:SetVisible(false) end
|
||||
option._submenu_open = false
|
||||
if submenu.Items then
|
||||
for _, item in ipairs(submenu.Items) do
|
||||
if item.CloseSubMenu then item:CloseSubMenu() end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function isAnySubmenuHovered(opt)
|
||||
if not IsValid(opt) then return false end
|
||||
if opt:IsHovered() then return true end
|
||||
if opt._submenu and IsValid(opt._submenu) and opt._submenu:IsVisible() then
|
||||
if isAnySubmenuHovered(opt._submenu) then return true end
|
||||
for _, item in ipairs(opt._submenu.Items or {}) do
|
||||
if isAnySubmenuHovered(item) then return true end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
option.OnCursorExited = function(pnl)
|
||||
timer.Simple(0.15, function()
|
||||
if not isAnySubmenuHovered(pnl) then
|
||||
if IsValid(pnl) then
|
||||
pnl:CloseSubMenu()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
submenu.OnCursorExited = function(pnl)
|
||||
timer.Simple(0.15, function()
|
||||
if not isAnySubmenuHovered(option) then
|
||||
if IsValid(pnl) then
|
||||
option:CloseSubMenu()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return submenu
|
||||
end
|
||||
|
||||
option.AddSubMenu = option.AddSubMenu
|
||||
|
||||
if optData then
|
||||
for k, v in pairs(optData) do
|
||||
option[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local iconMat
|
||||
|
||||
if option.Icon then
|
||||
iconMat = type(option.Icon) == 'IMaterial' and option.Icon or Material(option.Icon)
|
||||
end
|
||||
|
||||
option.Paint = function(pnl, w, h)
|
||||
w = w or pnl:GetWide()
|
||||
h = h or pnl:GetTall()
|
||||
|
||||
if pnl:IsHovered() then
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(5, 20)
|
||||
:Draw()
|
||||
end
|
||||
RNDX.Draw(16, 0, 0, w, h, Mantle.color.hover, RNDX.SHAPE_IOS)
|
||||
|
||||
if pnl._submenu and not pnl._submenu_open then
|
||||
pnl:OpenSubMenu()
|
||||
end
|
||||
end
|
||||
|
||||
if iconMat then
|
||||
local iconSize = 16
|
||||
RNDX.DrawMaterial(0, 10, (h - iconSize) / 2, iconSize, iconSize, color_white, iconMat)
|
||||
end
|
||||
|
||||
draw.SimpleText(pnl.Text, 'Fated.18', pnl.Icon and 32 or 14, h * 0.5, Mantle.color.text, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
table.insert(self.Items, option)
|
||||
|
||||
self:UpdateSize()
|
||||
|
||||
return option
|
||||
end
|
||||
|
||||
function PANEL:AddSpacer()
|
||||
local spacer = vgui.Create('DPanel', self)
|
||||
spacer:Dock(TOP)
|
||||
spacer:DockMargin(8, 6, 8, 6)
|
||||
spacer:SetTall(1)
|
||||
spacer.sumTall = 13
|
||||
spacer.Paint = function(_, w, h)
|
||||
RNDX.Draw(0, 0, 0, w, h, Mantle.color.focus_panel)
|
||||
end
|
||||
|
||||
table.insert(self.Items, spacer)
|
||||
self:UpdateSize()
|
||||
|
||||
return spacer
|
||||
end
|
||||
|
||||
function PANEL:UpdateSize()
|
||||
local height = 12
|
||||
for _, item in ipairs(self.Items) do
|
||||
if IsValid(item) then
|
||||
height = height + (item.sumTall or item:GetTall())
|
||||
end
|
||||
end
|
||||
|
||||
local maxWidth = math.max(160, self.MaxTextWidth + 56)
|
||||
self:SetSize(maxWidth, math.min(height, ScrH() * 0.8))
|
||||
|
||||
if not self._targetX or not self._targetY then
|
||||
Mantle.func.ClampMenuPosition(self)
|
||||
self._targetX, self._targetY = self:GetPos()
|
||||
if not self._initPosSet then
|
||||
self:SetPos(self._targetX, self._targetY + 6)
|
||||
end
|
||||
else
|
||||
Mantle.func.ClampMenuPosition(self)
|
||||
self._targetX, self._targetY = self:GetPos()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Open()
|
||||
-- Clear
|
||||
end
|
||||
|
||||
function PANEL:CloseMenu()
|
||||
if self._closing then return end
|
||||
self._closing = true
|
||||
self._disableBlur = true
|
||||
self._animTarget = 0
|
||||
end
|
||||
|
||||
function PANEL:GetDeleteSelf()
|
||||
return true
|
||||
end
|
||||
|
||||
vgui.Register('MantleDermaMenu', PANEL, 'DPanel')
|
||||
|
||||
function Mantle.ui.derma_menu()
|
||||
if IsValid(Mantle.ui.menu_derma_menu) then
|
||||
Mantle.ui.menu_derma_menu:CloseMenu()
|
||||
end
|
||||
|
||||
local mouseX, mouseY = input.GetCursorPos()
|
||||
local m = vgui.Create('MantleDermaMenu')
|
||||
m:SetPos(mouseX, mouseY)
|
||||
|
||||
Mantle.func.ClampMenuPosition(m)
|
||||
|
||||
m._targetX, m._targetY = m:GetPos()
|
||||
Mantle.ui.menu_derma_menu = m
|
||||
|
||||
return m
|
||||
end
|
||||
@@ -0,0 +1,98 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.title = nil
|
||||
self.placeholder = Mantle.lang.get('mantle', 'entry_default_placeholder')
|
||||
self:SetTall(26)
|
||||
self.action = function() end
|
||||
|
||||
local font = 'Fated.18'
|
||||
|
||||
self.textEntry = vgui.Create('DTextEntry', self)
|
||||
self.textEntry:Dock(FILL)
|
||||
self.textEntry:SetText('')
|
||||
self.textEntry.OnCloseFocus = function()
|
||||
self.action(self:GetValue())
|
||||
end
|
||||
|
||||
self._text_offset = 0
|
||||
self._shadowLerp = 5
|
||||
|
||||
self.textEntry.Paint = nil
|
||||
self.textEntry.PaintOver = function(s, w, h)
|
||||
local ft = FrameTime()
|
||||
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
local target = s:IsEditing() and 10 or 5
|
||||
self._shadowLerp = Mantle.func.approachExp(self._shadowLerp, target, 12, ft)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Shadow(self._shadowLerp, 20)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.focus_panel)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
|
||||
local value = self:GetValue() or ''
|
||||
surface.SetFont(font)
|
||||
local padding = 6
|
||||
local available_w = w - padding * 2
|
||||
|
||||
local caret = #value
|
||||
local before_caret = string.sub(value, 1, caret)
|
||||
local caret_x = surface.GetTextSize(before_caret)
|
||||
local text_w = surface.GetTextSize(value)
|
||||
|
||||
local desired_offset = 0
|
||||
if caret_x > available_w then
|
||||
desired_offset = caret_x - available_w
|
||||
end
|
||||
if text_w - desired_offset < available_w then
|
||||
desired_offset = math.max(0, text_w - available_w)
|
||||
end
|
||||
|
||||
self._text_offset = Mantle.func.approachExp(self._text_offset or 0, desired_offset, 24, ft)
|
||||
|
||||
local text = self.placeholder
|
||||
local col = Mantle.color.gray
|
||||
if value != '' then
|
||||
text = value
|
||||
col = Mantle.color.text
|
||||
end
|
||||
|
||||
draw.SimpleText(text, font, padding - self._text_offset, h * 0.5, col, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetTitle(title)
|
||||
self.title = title
|
||||
self:SetTall(52)
|
||||
|
||||
if IsValid(self.titlePanel) then
|
||||
self.titlePanel:Remove()
|
||||
end
|
||||
|
||||
self.titlePanel = vgui.Create('DPanel', self)
|
||||
self.titlePanel:Dock(TOP)
|
||||
self.titlePanel:DockMargin(0, 0, 0, 6)
|
||||
self.titlePanel:SetTall(18)
|
||||
self.titlePanel.Paint = function(_, w, h)
|
||||
draw.SimpleText(self.title, 'Fated.18', 0, 0, Mantle.color.text)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetPlaceholder(placeholder)
|
||||
self.placeholder = placeholder
|
||||
end
|
||||
|
||||
function PANEL:GetValue()
|
||||
return self.textEntry:GetText()
|
||||
end
|
||||
|
||||
vgui.Register('MantleEntry', PANEL, 'EditablePanel')
|
||||
@@ -0,0 +1,194 @@
|
||||
local PANEL = {}
|
||||
|
||||
local mat_close = Material('mantle/close_btn_new.png')
|
||||
|
||||
function PANEL:Init()
|
||||
self.bool_alpha = true
|
||||
self.bool_lite = false
|
||||
self.title = Mantle.lang.get('mantle', 'frame_title')
|
||||
self.center_title = ''
|
||||
|
||||
self:DockPadding(6, 30, 6, 6)
|
||||
|
||||
self.top_panel = vgui.Create('DButton', self)
|
||||
self.top_panel:SetText('')
|
||||
self.top_panel:SetCursor('sizeall')
|
||||
self.top_panel.Paint = nil
|
||||
self.top_panel.OnMousePressed = function(s, key)
|
||||
if key == MOUSE_LEFT then
|
||||
self.Dragging = {gui.MouseX() - self.x, gui.MouseY() - self.y}
|
||||
s:MouseCapture(true)
|
||||
self:SetAlpha(200)
|
||||
end
|
||||
end
|
||||
self.top_panel.OnMouseReleased = function(s, key)
|
||||
if key == MOUSE_LEFT then
|
||||
self.Dragging = nil
|
||||
s:MouseCapture(false)
|
||||
self:SetAlpha(255)
|
||||
end
|
||||
end
|
||||
self.top_panel.Think = function(s)
|
||||
if self.Dragging then
|
||||
local mouseX, mouseY = gui.MousePos()
|
||||
local newPosX, newPosY = mouseX - self.Dragging[1], mouseY - self.Dragging[2]
|
||||
|
||||
self:SetPos(newPosX, newPosY)
|
||||
end
|
||||
end
|
||||
|
||||
self.cls = vgui.Create('Button', self)
|
||||
self.cls:SetText('')
|
||||
self.cls.Paint = function(_, w, h)
|
||||
RNDX().Rect(2, 2, w - 4, h - 4)
|
||||
:Color(Mantle.color.header_text)
|
||||
:Material(mat_close)
|
||||
:Draw()
|
||||
end
|
||||
self.cls.DoClick = function()
|
||||
self:AlphaTo(0, 0.1, 0, function()
|
||||
self:Remove()
|
||||
end)
|
||||
|
||||
Mantle.func.sound()
|
||||
end
|
||||
self.cls.DoRightClick = function()
|
||||
local DM = Mantle.ui.derma_menu()
|
||||
|
||||
DM:AddOption(Mantle.lang.get('mantle', 'frame_alpha'), function()
|
||||
self.bool_alpha = !self.bool_alpha
|
||||
end, self.bool_alpha and 'icon16/bullet_green.png' or 'icon16/bullet_red.png')
|
||||
|
||||
local boolInput = self:IsKeyboardInputEnabled()
|
||||
DM:AddOption(Mantle.lang.get('mantle', 'frame_move_from_menu'), function()
|
||||
self:SetKeyBoardInputEnabled(!boolInput)
|
||||
end, !boolInput and 'icon16/bullet_green.png' or 'icon16/bullet_red.png')
|
||||
|
||||
DM:AddOption(Mantle.lang.get('mantle', 'frame_close_window'), function()
|
||||
self:Remove()
|
||||
end, 'icon16/cross.png')
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetAlphaBackground(is_alpha)
|
||||
self.bool_alpha = is_alpha
|
||||
end
|
||||
|
||||
function PANEL:SetTitle(title)
|
||||
self.title = title
|
||||
end
|
||||
|
||||
function PANEL:SetCenterTitle(center_title)
|
||||
self.center_title = center_title
|
||||
end
|
||||
|
||||
function PANEL:ShowAnimation()
|
||||
Mantle.func.animate_appearance(self, self:GetWide(), self:GetTall(), 0.3, 0.2)
|
||||
end
|
||||
|
||||
function PANEL:DisableCloseBtn()
|
||||
self.cls:SetVisible(false)
|
||||
end
|
||||
|
||||
function PANEL:SetDraggable(is_draggable)
|
||||
self.top_panel:SetVisible(is_draggable)
|
||||
end
|
||||
|
||||
function PANEL:LiteMode()
|
||||
self.bool_lite = true
|
||||
self:DockPadding(6, 6, 6, 6)
|
||||
|
||||
self.cls:SetZPos(2)
|
||||
end
|
||||
|
||||
function PANEL:Notify(text, duration, col)
|
||||
if IsValid(self.messagePanel) then self.messagePanel:Remove() end
|
||||
duration = duration or 2
|
||||
col = col or Mantle.color.theme
|
||||
|
||||
surface.SetFont('Fated.20')
|
||||
local tw, th = surface.GetTextSize(text)
|
||||
|
||||
local mp = vgui.Create('DPanel', self)
|
||||
mp:SetSize(tw + 16, th + 8)
|
||||
mp:SetMouseInputEnabled(false)
|
||||
local startY = self:GetTall() + mp:GetTall()
|
||||
local endY = self:GetTall() - mp:GetTall() - 16
|
||||
mp:SetPos((self:GetWide() - mp:GetWide()) * 0.5, startY)
|
||||
mp:SetAlpha(0)
|
||||
mp.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(col)
|
||||
:Shadow(7, 20)
|
||||
:Outline(3)
|
||||
:Clip(self)
|
||||
:Draw()
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(col)
|
||||
:Draw()
|
||||
draw.SimpleText(text, 'Fated.20', w * 0.5, h * 0.5 - 1, Mantle.color.text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
mp:MoveTo(mp.x, endY, 0.3, 0, 0.7)
|
||||
mp:AlphaTo(255, 0.3, 0, function()
|
||||
timer.Simple(duration, function()
|
||||
if !IsValid(mp) then return end
|
||||
mp:AlphaTo(0, 0.25, 0, function()
|
||||
if IsValid(mp) then
|
||||
mp:Remove()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
self.messagePanel = mp
|
||||
end
|
||||
|
||||
local flagsHeader = RNDX.NO_BL + RNDX.NO_BR
|
||||
local flagsBackground = RNDX.NO_TL + RNDX.NO_TR
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(6)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shadow(10, 16)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
if !self.bool_lite then
|
||||
RNDX().Rect(0, 0, w, 24)
|
||||
:Radii(6, 6, 0, 0)
|
||||
:Color(Mantle.color.header)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local headerTall = self.bool_lite and 0 or 24
|
||||
if self.bool_alpha and Mantle.ui.convar.blur then
|
||||
RNDX().Rect(0, headerTall, w, h - headerTall)
|
||||
:Radii(self.bool_lite and 6 or 0, self.bool_lite and 6 or 0, 6, 6)
|
||||
:Blur()
|
||||
:Draw()
|
||||
end
|
||||
RNDX().Rect(0, headerTall, w, h - headerTall)
|
||||
:Radii(self.bool_lite and 6 or 0, self.bool_lite and 6 or 0, 6, 6)
|
||||
:Color((self.bool_alpha and Mantle.ui.convar.blur) and Mantle.color.background_alpha or Mantle.color.background)
|
||||
:Draw()
|
||||
|
||||
if !self.bool_lite then
|
||||
if self.center_title != '' then
|
||||
draw.SimpleText(self.center_title, 'Fated.20b', w * 0.5, 12, Mantle.color.header_text, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
draw.SimpleText(self.title, 'Fated.16', 6, 4, Mantle.color.header_text)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
self.top_panel:SetSize(w, 24)
|
||||
|
||||
self.cls:SetSize(20, 20)
|
||||
self.cls:SetPos(w - 22, 2)
|
||||
end
|
||||
|
||||
vgui.Register('MantleFrame', PANEL, 'EditablePanel')
|
||||
@@ -0,0 +1,115 @@
|
||||
local color_disconnect = Color(210, 65, 65)
|
||||
local color_bot = Color(70, 150, 220)
|
||||
local color_online = Color(120, 180, 70)
|
||||
|
||||
function Mantle.ui.player_selector(do_click, func_check)
|
||||
if IsValid(Mantle.ui.menu_player_selector) then
|
||||
Mantle.ui.menu_player_selector:Remove()
|
||||
end
|
||||
|
||||
Mantle.ui.menu_player_selector = vgui.Create('MantleFrame')
|
||||
Mantle.ui.menu_player_selector:SetSize(340, 398)
|
||||
Mantle.ui.menu_player_selector:Center()
|
||||
Mantle.ui.menu_player_selector:MakePopup()
|
||||
Mantle.ui.menu_player_selector:SetTitle('')
|
||||
Mantle.ui.menu_player_selector:SetCenterTitle(Mantle.lang.get('mantle', 'player_title'))
|
||||
Mantle.ui.menu_player_selector:ShowAnimation()
|
||||
|
||||
local contentPanel = vgui.Create('Panel', Mantle.ui.menu_player_selector)
|
||||
contentPanel:Dock(FILL)
|
||||
contentPanel:DockMargin(8, 0, 8, 8)
|
||||
Mantle.ui.menu_player_selector.sp = vgui.Create('MantleScrollPanel', contentPanel)
|
||||
Mantle.ui.menu_player_selector.sp:Dock(FILL)
|
||||
|
||||
local CARD_HEIGHT = 44
|
||||
local AVATAR_SIZE = 32
|
||||
local AVATAR_X = 14
|
||||
|
||||
local function CreatePlayerCard(pl)
|
||||
local card = vgui.Create('DButton', Mantle.ui.menu_player_selector.sp)
|
||||
card:Dock(TOP)
|
||||
card:DockMargin(0, 5, 0, 0)
|
||||
card:SetTall(CARD_HEIGHT)
|
||||
card:SetText('')
|
||||
card.hover_status = 0
|
||||
card.OnCursorEntered = function(self)
|
||||
self:SetCursor('hand')
|
||||
end
|
||||
card.OnCursorExited = function(self)
|
||||
self:SetCursor('arrow')
|
||||
end
|
||||
card.Think = function(self)
|
||||
local target = self:IsHovered() and 1 or 0
|
||||
self.hover_status = Mantle.func.approachExp(self.hover_status, target, 8, FrameTime())
|
||||
end
|
||||
card.DoClick = function()
|
||||
if IsValid(pl) then
|
||||
Mantle.func.sound()
|
||||
do_click(pl)
|
||||
end
|
||||
Mantle.ui.menu_player_selector:Remove()
|
||||
end
|
||||
card.pl_color = team.GetColor(pl:Team()) or color_online
|
||||
card.Paint = function(self, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(10)
|
||||
:Color(Mantle.color.panel[1])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
if self.hover_status > 0 then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(10)
|
||||
:Color(Color(0, 0, 0, 40 * self.hover_status))
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local infoX = AVATAR_X + AVATAR_SIZE + 10
|
||||
|
||||
if !IsValid(pl) then
|
||||
draw.SimpleText(Mantle.lang.get('mantle', 'player_offline'), 'Fated.18', infoX, h * 0.5, color_disconnect, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
return
|
||||
end
|
||||
|
||||
draw.SimpleText(pl:Name(), 'Fated.18', infoX, 6, Mantle.color.text, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
local group = pl:GetUserGroup() or 'user'
|
||||
group = string.upper(string.sub(group, 1, 1)) .. string.sub(group, 2)
|
||||
draw.SimpleText(group, 'Fated.14', infoX, h - 6, Mantle.color.gray, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
draw.SimpleText(pl:Ping() .. ' ' .. Mantle.lang.get('mantle', 'player_ping'), 'Fated.16', w - 20, h - 6, Mantle.color.gray, TEXT_ALIGN_RIGHT, TEXT_ALIGN_BOTTOM)
|
||||
|
||||
local statusColor = color_disconnect
|
||||
if pl:IsBot() then
|
||||
statusColor = color_bot
|
||||
else
|
||||
statusColor = self.pl_color
|
||||
end
|
||||
|
||||
RNDX.DrawCircle(w - 24, 14, 12, statusColor)
|
||||
end
|
||||
|
||||
local avatarImg = vgui.Create('AvatarImage', card)
|
||||
avatarImg:SetSize(AVATAR_SIZE, AVATAR_SIZE)
|
||||
avatarImg:SetPos(AVATAR_X, (CARD_HEIGHT - AVATAR_SIZE) * 0.5)
|
||||
avatarImg:SetSteamID(pl:SteamID64(), 64)
|
||||
avatarImg:SetMouseInputEnabled(false)
|
||||
avatarImg:SetKeyboardInputEnabled(false)
|
||||
avatarImg.PaintOver = function() end
|
||||
avatarImg:SetPos(AVATAR_X, (card:GetTall() - AVATAR_SIZE) * 0.5)
|
||||
|
||||
return card
|
||||
end
|
||||
|
||||
for _, pl in player.Iterator() do
|
||||
CreatePlayerCard(pl)
|
||||
end
|
||||
|
||||
Mantle.ui.menu_player_selector.btn_close = vgui.Create('MantleBtn', Mantle.ui.menu_player_selector)
|
||||
Mantle.ui.menu_player_selector.btn_close:Dock(BOTTOM)
|
||||
Mantle.ui.menu_player_selector.btn_close:DockMargin(16, 8, 16, 12)
|
||||
Mantle.ui.menu_player_selector.btn_close:SetTall(36)
|
||||
Mantle.ui.menu_player_selector.btn_close:SetTxt(Mantle.lang.get('mantle', 'player_close'))
|
||||
Mantle.ui.menu_player_selector.btn_close:SetColorHover(color_disconnect)
|
||||
Mantle.ui.menu_player_selector.btn_close.DoClick = function()
|
||||
Mantle.ui.menu_player_selector:Remove()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,458 @@
|
||||
local PANEL = {}
|
||||
|
||||
local pi = math.pi
|
||||
local math_cos = math.cos
|
||||
local math_sin = math.sin
|
||||
local math_atan2 = math.atan2
|
||||
local math_sqrt = math.sqrt
|
||||
local math_floor = math.floor
|
||||
local math_min = math.min
|
||||
local math_max = math.max
|
||||
local FrameTime = FrameTime
|
||||
local SysTime = SysTime
|
||||
local CurTime = CurTime
|
||||
local Lerp = Lerp
|
||||
|
||||
local EPS = 1e-6
|
||||
local EPS_ANGLE = 1e-4
|
||||
|
||||
local function GetSectorIndexFromAngle(angle, cnt)
|
||||
if !angle or cnt <= 0 then return nil end
|
||||
local sector = (2 * pi) / cnt
|
||||
local raw = angle / sector
|
||||
local idx = (math_floor(raw + EPS) % cnt) + 1
|
||||
return idx
|
||||
end
|
||||
|
||||
local function ClampEndAngle(a)
|
||||
if a >= 360 then
|
||||
return 360 - EPS_ANGLE
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
function PANEL:Init(options)
|
||||
options = options or {}
|
||||
|
||||
self.options = {}
|
||||
self.rootMenu = { title = 'Меню', desc = 'Выберите опцию', options = self.options }
|
||||
self.menuStack = {}
|
||||
self.currentMenu = self.rootMenu
|
||||
|
||||
local baseRadius = options.radius or 320
|
||||
local baseInner = options.inner_radius or 110
|
||||
|
||||
local minW, minH = 1366, 768
|
||||
local scale = 1
|
||||
if Mantle.func.sw > minW and Mantle.func.sh > minH then
|
||||
scale = math_min(math_min(Mantle.func.sw / 1920, Mantle.func.sh / 1080), 1.15)
|
||||
end
|
||||
|
||||
self.radius = Mantle.func.w(baseRadius) * scale
|
||||
self.innerRadius = Mantle.func.w(baseInner) * scale
|
||||
self.scale = scale
|
||||
|
||||
self.titleFont = 'Fated.28'
|
||||
self.font = 'Fated.20'
|
||||
self.descFont = 'Fated.14'
|
||||
|
||||
self.fadeInTime = 0.18
|
||||
self.openTime = SysTime()
|
||||
self.currentAlpha = 0
|
||||
self.scaleAnim = 0.96
|
||||
self.scale_animation = options.scale_animation != false
|
||||
|
||||
self.disable_background = options.disable_background or false
|
||||
self.hover_sound = options.hover_sound or 'mantle/ratio_btn.ogg'
|
||||
|
||||
self.hoverOption = nil
|
||||
self.hoverAnim = 0
|
||||
self.selectedOption = nil
|
||||
|
||||
self._hotkeyCooldown = {}
|
||||
|
||||
self.optionHover = {}
|
||||
|
||||
self:SetSize(Mantle.func.sw, Mantle.func.sh)
|
||||
self:SetPos(0, 0)
|
||||
self:MakePopup()
|
||||
self:SetKeyboardInputEnabled(true)
|
||||
self:SetDrawOnTop(true)
|
||||
self:SetMouseInputEnabled(true)
|
||||
|
||||
self._mouseWasDown = false
|
||||
|
||||
self.Think = function()
|
||||
if self.currentAlpha < 255 then
|
||||
self.currentAlpha = math.Clamp(255 * ((SysTime() - self.openTime) / self.fadeInTime), 0, 255)
|
||||
if self.scale_animation then
|
||||
local t = math.Clamp((SysTime() - self.openTime) / self.fadeInTime, 0, 1)
|
||||
self.scaleAnim = 0.96 + (1 - (1 - t)^2) * 0.04
|
||||
else
|
||||
self.scaleAnim = 1
|
||||
end
|
||||
end
|
||||
|
||||
local curOuter = self.radius * self.scaleAnim
|
||||
local curInner = self.innerRadius * self.scaleAnim
|
||||
|
||||
local mouseDown = input.IsMouseDown(MOUSE_LEFT)
|
||||
if mouseDown and !self._mouseWasDown then
|
||||
local mx, my = self:CursorPos()
|
||||
local cx, cy = Mantle.func.sw / 2, Mantle.func.sh / 2
|
||||
local dist = math_sqrt((mx - cx)^2 + (my - cy)^2)
|
||||
|
||||
if dist > curInner and dist < curOuter then
|
||||
local ang = math_atan2(my - cy, mx - cx)
|
||||
if ang < 0 then ang = ang + 2 * pi end
|
||||
local opts = self:GetCurrentOptions()
|
||||
local cnt = #opts
|
||||
if cnt > 0 then
|
||||
local idx = GetSectorIndexFromAngle(ang, cnt)
|
||||
if idx and opts[idx] then
|
||||
self:SelectOption(idx)
|
||||
if self.hover_sound then surface.PlaySound(self.hover_sound) end
|
||||
end
|
||||
end
|
||||
elseif dist <= curInner then
|
||||
if #self.menuStack > 0 then
|
||||
self:GoBack()
|
||||
if self.hover_sound then surface.PlaySound(self.hover_sound) end
|
||||
else
|
||||
self:Remove()
|
||||
end
|
||||
else
|
||||
if dist >= curOuter then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local mx, my = self:CursorPos()
|
||||
local cx, cy = Mantle.func.sw / 2, Mantle.func.sh / 2
|
||||
local dist = math_sqrt((mx - cx)^2 + (my - cy)^2)
|
||||
local hovered = nil
|
||||
if dist > curInner and dist < curOuter then
|
||||
local ang = math_atan2(my - cy, mx - cx)
|
||||
if ang < 0 then ang = ang + 2 * pi end
|
||||
local opts = self:GetCurrentOptions()
|
||||
local cnt = #opts
|
||||
if cnt > 0 then
|
||||
hovered = GetSectorIndexFromAngle(ang, cnt)
|
||||
end
|
||||
end
|
||||
|
||||
if self.hoverOption != hovered and hovered and self.hover_sound then
|
||||
surface.PlaySound(self.hover_sound)
|
||||
end
|
||||
|
||||
self.hoverOption = hovered
|
||||
self.hoverAnim = math.Clamp(self.hoverAnim + (self.hoverOption and 10 or -20) * FrameTime(), 0, 1)
|
||||
self._mouseWasDown = mouseDown
|
||||
|
||||
local dt = FrameTime()
|
||||
local opts = self:GetCurrentOptions()
|
||||
for i = 1, #opts do
|
||||
local target = (self.hoverOption == i) and 1 or 0
|
||||
self.optionHover[i] = Mantle.func.approachExp(self.optionHover[i] or 0, target, 18, dt)
|
||||
end
|
||||
|
||||
for i = 1, math_min(9, #self:GetCurrentOptions()) do
|
||||
local k = KEY_1 + (i-1)
|
||||
if input.IsKeyDown(k) then
|
||||
local last = self._hotkeyCooldown[k] or 0
|
||||
if CurTime() - last > 0.18 then
|
||||
self._hotkeyCooldown[k] = CurTime()
|
||||
self:SelectOption(i)
|
||||
if self.hover_sound then surface.PlaySound(self.hover_sound) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(k)
|
||||
local mx, my = self:CursorPos()
|
||||
local cx, cy = Mantle.func.sw / 2, Mantle.func.sh / 2
|
||||
local curOuter = self.radius * self.scaleAnim
|
||||
local dist = math_sqrt((mx - cx)^2 + (my - cy)^2)
|
||||
if dist <= curOuter then return self:MouseCapture(true) end
|
||||
self:Remove()
|
||||
return true
|
||||
end
|
||||
function PANEL:OnMouseReleased(k) self:MouseCapture(false) end
|
||||
|
||||
function PANEL:CreateSubMenu(title, desc)
|
||||
local submenu = { title = title or 'Подменю', desc = desc or '', options = {} }
|
||||
function submenu:AddOption(text, func, icon, desc)
|
||||
table.insert(submenu.options, { text = text, func = func, icon = icon, desc = desc })
|
||||
return #submenu.options
|
||||
end
|
||||
return submenu
|
||||
end
|
||||
|
||||
function PANEL:AddSubMenuOption(text, submenu, icon, desc)
|
||||
return self:AddOption(text, nil, icon, desc, submenu)
|
||||
end
|
||||
|
||||
function PANEL:AddOption(text, func, icon, desc, submenu)
|
||||
table.insert(self.options, { text = text, func = func, icon = icon, desc = desc, submenu = submenu })
|
||||
return #self.options
|
||||
end
|
||||
|
||||
function PANEL:GetCurrentOptions()
|
||||
if self.currentMenu and self.currentMenu.options then
|
||||
return self.currentMenu.options
|
||||
end
|
||||
return self.options
|
||||
end
|
||||
|
||||
function PANEL:SelectOption(index)
|
||||
local opts = self:GetCurrentOptions()
|
||||
if !opts or !opts[index] then return end
|
||||
local opt = opts[index]
|
||||
if opt.submenu then
|
||||
table.insert(self.menuStack, self.currentMenu)
|
||||
self.currentMenu = opt.submenu
|
||||
self:UpdateCenterText()
|
||||
return
|
||||
end
|
||||
self.selectedOption = opt
|
||||
if opt.func then
|
||||
local ok, err = pcall(opt.func)
|
||||
if !ok then ErrorNoHalt(tostring(err) .. '\n') end
|
||||
end
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function PANEL:GoBack()
|
||||
if #self.menuStack > 0 then
|
||||
self.currentMenu = table.remove(self.menuStack)
|
||||
self:UpdateCenterText()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetCenterText(title, desc)
|
||||
self.rootMenu.title = title or self.rootMenu.title
|
||||
self.rootMenu.desc = desc or self.rootMenu.desc
|
||||
self:UpdateCenterText()
|
||||
end
|
||||
|
||||
function PANEL:UpdateCenterText()
|
||||
if self.currentMenu then
|
||||
self.centerText = self.currentMenu.title or self.rootMenu.title
|
||||
self.centerDesc = self.currentMenu.desc or self.rootMenu.desc
|
||||
else
|
||||
self.centerText = self.rootMenu.title
|
||||
self.centerDesc = self.rootMenu.desc
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:IsMouseOver()
|
||||
local mx, my = self:CursorPos()
|
||||
local cx, cy = Mantle.func.sw / 2, Mantle.func.sh / 2
|
||||
local curOuter = self.radius * self.scaleAnim
|
||||
return math_sqrt((mx - cx)^2 + (my - cy)^2) <= curOuter
|
||||
end
|
||||
|
||||
function PANEL:OnCursorMoved(x,y)
|
||||
if !self:IsMouseOver() then self.hoverOption = nil end
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
if Mantle.ui.menu_radial == self then Mantle.ui.menu_radial = nil end
|
||||
end
|
||||
|
||||
function PANEL:Paint(w,h)
|
||||
local cx, cy = Mantle.func.sw / 2, Mantle.func.sh / 2
|
||||
local alpha = math.Clamp(self.currentAlpha / 255, 0, 1)
|
||||
local opts = self:GetCurrentOptions()
|
||||
local cnt = #opts
|
||||
|
||||
if !self.disable_background then
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Radii(0, 0, 0, 0)
|
||||
:Color(Color(0, 0, 0, 140 * alpha))
|
||||
:Draw()
|
||||
end
|
||||
|
||||
local outerR = self.radius * self.scaleAnim
|
||||
local innerR = self.innerRadius * self.scaleAnim
|
||||
local outerD = outerR * 2
|
||||
local innerD = innerR * 2
|
||||
|
||||
RNDX().Circle(cx, cy, outerD + 12)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shadow(8, 24)
|
||||
:Draw()
|
||||
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:Color(Color(Mantle.color.background.r, Mantle.color.background.g, Mantle.color.background.b, math_floor(240 * alpha)))
|
||||
:Draw()
|
||||
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:Outline(2)
|
||||
:Color(Color(Mantle.color.theme.r, Mantle.color.theme.g, Mantle.color.theme.b, math_floor(160 * alpha)))
|
||||
:Draw()
|
||||
|
||||
if cnt > 0 then
|
||||
local sectorDeg = 360 / cnt
|
||||
local baseCol = Mantle.color.background_panelpopup
|
||||
local baseSectorCol = Color(baseCol.r, baseCol.g, baseCol.b, math_floor(255 * alpha))
|
||||
|
||||
for i = 1, cnt do
|
||||
local startDeg = (i-1) * sectorDeg
|
||||
local endDeg = i * sectorDeg
|
||||
if startDeg < 0 then startDeg = 0 end
|
||||
endDeg = ClampEndAngle(endDeg)
|
||||
if endDeg > startDeg then
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:StartAngle(startDeg)
|
||||
:EndAngle(endDeg)
|
||||
:Color(baseSectorCol)
|
||||
:Draw()
|
||||
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:StartAngle(startDeg)
|
||||
:EndAngle(endDeg)
|
||||
:Outline(2)
|
||||
:Color(Color(Mantle.color.panel[1].r, Mantle.color.panel[1].g, Mantle.color.panel[1].b, math_floor(160 * alpha)))
|
||||
:Draw()
|
||||
end
|
||||
end
|
||||
|
||||
if self.hoverOption and opts[self.hoverOption] then
|
||||
local i = self.hoverOption
|
||||
local startDeg = (i-1) * sectorDeg
|
||||
local endDeg = i * sectorDeg
|
||||
if startDeg < 0 then startDeg = 0 end
|
||||
endDeg = ClampEndAngle(endDeg)
|
||||
if endDeg > startDeg then
|
||||
local th = Mantle.color.theme
|
||||
local hoverAlpha = math_floor(200 * self.hoverAnim * alpha)
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:StartAngle(startDeg)
|
||||
:EndAngle(endDeg)
|
||||
:Color(Color(th.r, th.g, th.b, math_floor(22 * self.hoverAnim * alpha)))
|
||||
:Draw()
|
||||
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:StartAngle(startDeg)
|
||||
:EndAngle(endDeg)
|
||||
:Outline(2)
|
||||
:Color(Color(th.r, th.g, th.b, hoverAlpha))
|
||||
:Draw()
|
||||
end
|
||||
end
|
||||
|
||||
RNDX().Circle(cx, cy, innerD)
|
||||
:Color(Color(Mantle.color.background_panelpopup.r, Mantle.color.background_panelpopup.g, Mantle.color.background_panelpopup.b, math_floor(255 * alpha)))
|
||||
:Draw()
|
||||
|
||||
local tintA = math_floor(36 * alpha)
|
||||
RNDX().Circle(cx, cy, innerD - 8)
|
||||
:Color(Color(Mantle.color.theme.r, Mantle.color.theme.g, Mantle.color.theme.b, tintA))
|
||||
:Draw()
|
||||
|
||||
RNDX().Circle(cx, cy, innerD)
|
||||
:Outline(2)
|
||||
:Color(Color(Mantle.color.theme.r, Mantle.color.theme.g, Mantle.color.theme.b, math_floor(80 * alpha)))
|
||||
:Draw()
|
||||
|
||||
local sectorRad = (2 * pi) / cnt
|
||||
for i, option in ipairs(opts) do
|
||||
local startA = (i - 1) * sectorRad
|
||||
local midA = startA + sectorRad * 0.5
|
||||
|
||||
local hv = self.optionHover[i] or 0
|
||||
local eased = Mantle.func.easeOutCubic(math.Clamp(hv, 0, 1))
|
||||
|
||||
local labelR = innerR + (outerR - innerR) * (0.5 + 0.06 * eased)
|
||||
local numberR = innerR + (labelR - innerR) * 0.35
|
||||
local lx = cx + labelR * math_cos(midA)
|
||||
local ly = cy + labelR * math_sin(midA)
|
||||
local nx = cx + numberR * math_cos(midA)
|
||||
local ny = cy + numberR * math_sin(midA)
|
||||
local isHovered = (self.hoverOption == i)
|
||||
local txtAlpha = math_floor((isHovered and 255 or 220) * alpha)
|
||||
local txtCol = Color(Mantle.color.text.r, Mantle.color.text.g, Mantle.color.text.b, txtAlpha)
|
||||
|
||||
if option.icon and option.icon != false and option.icon != nil then
|
||||
local iconSize = Mantle.func.w(28) * self.scale * (1 + 0.06 * eased)
|
||||
local iconX = lx - iconSize * 0.5
|
||||
local iconY = ly - iconSize * 0.5 - Mantle.func.h(6) * self.scale
|
||||
local mat = Material(option.icon)
|
||||
if mat and !mat:IsError() then
|
||||
surface.SetDrawColor(255,255,255, math_floor(230 * alpha))
|
||||
surface.SetMaterial(mat)
|
||||
surface.DrawTexturedRect(iconX, iconY, iconSize, iconSize)
|
||||
end
|
||||
|
||||
draw.SimpleText(option.text or '', self.font, lx, ly + iconSize*0.5 - Mantle.func.h(4)*self.scale, txtCol, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
|
||||
if option.desc and isHovered then
|
||||
draw.SimpleText(option.desc, self.descFont, lx, ly + iconSize*0.5 + Mantle.func.h(16)*self.scale, Color(Mantle.color.header_text.r, Mantle.color.header_text.g, Mantle.color.header_text.b, math_floor(180 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
if i <= 9 then
|
||||
draw.SimpleText(tostring(i), 'Fated.14', nx, ny, Color(Mantle.color.theme.r, Mantle.color.theme.g, Mantle.color.theme.b, math_floor(200 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
else
|
||||
draw.SimpleText(option.text or '', self.font, lx, ly - Mantle.func.h(4) * self.scale, txtCol, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if option.desc and isHovered then
|
||||
draw.SimpleText(option.desc, self.descFont, lx, ly + Mantle.func.h(18) * self.scale, Color(Mantle.color.header_text.r, Mantle.color.header_text.g, Mantle.color.header_text.b, math_floor(180 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
if i <= 9 then
|
||||
draw.SimpleText(tostring(i), 'Fated.14', nx, ny, Color(Mantle.color.theme.r, Mantle.color.theme.g, Mantle.color.theme.b, math_floor(200 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
RNDX().Circle(cx, cy, outerD)
|
||||
:Color(Color(Mantle.color.background.r, Mantle.color.background.g, Mantle.color.background.b, math_floor(240 * alpha)))
|
||||
:Draw()
|
||||
RNDX().Circle(cx, cy, innerD)
|
||||
:Color(Color(Mantle.color.background_panelpopup.r, Mantle.color.background_panelpopup.g, Mantle.color.background_panelpopup.b, math_floor(255 * alpha)))
|
||||
:Draw()
|
||||
end
|
||||
|
||||
if self.selectedOption then
|
||||
local opt = self.selectedOption
|
||||
if opt.icon and opt.icon != false and opt.icon != nil then
|
||||
local isz = Mantle.func.w(48) * self.scale
|
||||
local mat = Material(opt.icon)
|
||||
if mat and !mat:IsError() then
|
||||
surface.SetDrawColor(255, 255, 255, math_floor(255 * alpha))
|
||||
surface.SetMaterial(mat)
|
||||
surface.DrawTexturedRect(cx - isz/2, cy - isz/2 - Mantle.func.h(6)*self.scale, isz, isz)
|
||||
end
|
||||
draw.SimpleText(opt.text or '', self.titleFont, cx + isz*0.6, cy - Mantle.func.h(6) * self.scale, Color(Mantle.color.text.r, Mantle.color.text.g, Mantle.color.text.b, math_floor(255 * alpha)), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
if opt.desc then
|
||||
draw.SimpleText(opt.desc, self.descFont, cx + isz*0.6, cy + Mantle.func.h(18) * self.scale, Color(Mantle.color.header_text.r, Mantle.color.header_text.g, Mantle.color.header_text.b, math_floor(180 * alpha)), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
else
|
||||
draw.SimpleText(opt.text or '', self.titleFont, cx, cy - Mantle.func.h(6) * self.scale, Color(Mantle.color.text.r, Mantle.color.text.g, Mantle.color.text.b, math_floor(255 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
if opt.desc then
|
||||
draw.SimpleText(opt.desc, self.descFont, cx, cy + Mantle.func.h(18) * self.scale, Color(Mantle.color.header_text.r, Mantle.color.header_text.g, Mantle.color.header_text.b, math_floor(180 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
else
|
||||
draw.SimpleText(self.centerText or self.rootMenu.title, self.titleFont, cx, cy - Mantle.func.h(8) * self.scale, Color(Mantle.color.text.r, Mantle.color.text.g, Mantle.color.text.b, math_floor(255 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText(self.centerDesc or self.rootMenu.desc, self.descFont, cx, cy + Mantle.func.h(18) * self.scale, Color(Mantle.color.header_text.r, Mantle.color.header_text.g, Mantle.color.header_text.b, math_floor(160 * alpha)), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register('MantleRadialPanel', PANEL, 'DPanel')
|
||||
|
||||
function Mantle.ui.radial_menu(options)
|
||||
if IsValid(Mantle.ui.menu_radial) then
|
||||
Mantle.ui.menu_radial:Remove()
|
||||
end
|
||||
|
||||
local m = vgui.Create('MantleRadialPanel')
|
||||
m:Init(options)
|
||||
Mantle.ui.menu_radial = m
|
||||
return m
|
||||
end
|
||||
@@ -0,0 +1,498 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self._vbarPadRight = 6
|
||||
|
||||
self.content = vgui.Create('Panel', self)
|
||||
self.content:SetMouseInputEnabled(true)
|
||||
|
||||
self.vbar = vgui.Create('Panel', self)
|
||||
self.vbar:SetMouseInputEnabled(true)
|
||||
|
||||
self.vbarDefaultWidth = 4
|
||||
self.vbarExpandedWidth = 6
|
||||
self.vbarWidthSpeed = 12
|
||||
self.vbarReserveWidth = self.vbarExpandedWidth
|
||||
|
||||
self.vbar:SetWide(self.vbarDefaultWidth)
|
||||
self.vbar.Dragging = false
|
||||
self.vbar._press_off = 0
|
||||
self.vbar:Dock(RIGHT)
|
||||
self.vbar:DockMargin(6, 0, 0, 0)
|
||||
self.vbar.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.focus_panel)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
self.vbarHoverDelay = 1
|
||||
self.vbarUnhoverDelay = 0.5
|
||||
|
||||
self.vbar._hoverEnter = 0
|
||||
self.vbar._hoverExit = 0
|
||||
self.vbar._expanded = false
|
||||
|
||||
self.vbar.btnGrip = vgui.Create('MantleBtn', self.vbar)
|
||||
self.vbar.btnGrip:SetText('')
|
||||
self.vbar.btnGrip._ShadowLerp = 0
|
||||
self.vbar.btnGrip.Paint = function(s, w, h)
|
||||
s._ShadowLerp = Lerp(FrameTime() * 10, s._ShadowLerp, self.vbar.Dragging and 7 or 0)
|
||||
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.theme)
|
||||
:Shadow(s._ShadowLerp, 20)
|
||||
:Draw()
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(32)
|
||||
:Color(Mantle.color.theme)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
self.vbar.btnGrip.OnMousePressed = function(s)
|
||||
local _, my = s:GetParent():CursorPos()
|
||||
s:GetParent().Dragging = true
|
||||
s:GetParent()._press_off = my - s.y
|
||||
s:MouseCapture(true)
|
||||
s:GetParent()._springing = false
|
||||
s:GetParent()._expanded = true
|
||||
end
|
||||
|
||||
self.vbar.btnGrip.OnMouseReleased = function(s)
|
||||
s:GetParent().Dragging = false
|
||||
s:MouseCapture(false)
|
||||
|
||||
if !(s:GetParent():IsHovered() or s:IsHovered()) then
|
||||
s:GetParent()._hoverExit = CurTime()
|
||||
end
|
||||
end
|
||||
|
||||
self.vbar.OnMousePressed = function(pnl)
|
||||
local _, my = pnl:CursorPos()
|
||||
local gy, gh = pnl.btnGrip.y, pnl.btnGrip:GetTall()
|
||||
if my < gy then
|
||||
self:_nudge(-self:GetTall())
|
||||
elseif my > gy + gh then
|
||||
self:_nudge(self:GetTall())
|
||||
end
|
||||
self.lastInput = CurTime()
|
||||
self._springing = false
|
||||
end
|
||||
|
||||
function self.vbar:AnimateTo(yPos)
|
||||
self:GetParent():SetScroll(yPos)
|
||||
end
|
||||
|
||||
function self.vbar:GetScroll()
|
||||
return self:GetParent():GetScroll()
|
||||
end
|
||||
|
||||
self.padL, self.padT, self.padR, self.padB = 0, 0, 0, 0
|
||||
self.offset = 0
|
||||
self.vel = 0
|
||||
self.drag = false
|
||||
self.dragLast = 0
|
||||
self.lastInput = 0
|
||||
|
||||
self.scrollStep = 500
|
||||
self.overscroll = 90
|
||||
self.overscrollThreshold = 50
|
||||
self.friction = 8
|
||||
self.spring = 5
|
||||
self.dragRes = 0.35
|
||||
self.gripMin = 28
|
||||
self.vbarSmooth = 3
|
||||
|
||||
self._vb_gripH = nil
|
||||
self._vb_gripY = nil
|
||||
self._vb_width = nil
|
||||
|
||||
self._needLayout = true
|
||||
|
||||
self:SetMouseInputEnabled(true)
|
||||
|
||||
self._springing = false
|
||||
self._springTarget = 0
|
||||
end
|
||||
|
||||
function PANEL:DockPadding(l,t,r,b)
|
||||
self.padL, self.padT, self.padR, self.padB = l or 0, t or 0, r or 0, b or 0
|
||||
self:_markDirty()
|
||||
end
|
||||
|
||||
function PANEL:_markDirty()
|
||||
self._needLayout = true
|
||||
end
|
||||
|
||||
function PANEL:GetCanvas()
|
||||
return self.content
|
||||
end
|
||||
|
||||
function PANEL:GetVBar()
|
||||
return self.vbar
|
||||
end
|
||||
|
||||
function PANEL:DisableVBarPadding()
|
||||
if !IsValid(self.vbar) then return end
|
||||
|
||||
self._vbarPadRight = 0
|
||||
|
||||
self.vbar:DockMargin(self._vbarPadRight, 0, 0, 0)
|
||||
self:_markDirty()
|
||||
self:InvalidateLayout(true)
|
||||
self.content:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:AddItem(pnl)
|
||||
pnl:SetParent(self.content)
|
||||
|
||||
local old = pnl.OnSizeChanged
|
||||
pnl.OnSizeChanged = function(...)
|
||||
if old then pcall(old, ...) end
|
||||
if IsValid(self) then
|
||||
self:_markDirty()
|
||||
self:InvalidateLayout(true)
|
||||
self.content:InvalidateLayout(true)
|
||||
self.content:SizeToChildren(false, true)
|
||||
end
|
||||
end
|
||||
|
||||
self:_markDirty()
|
||||
return pnl
|
||||
end
|
||||
|
||||
function PANEL:Add(pnl)
|
||||
return self:AddItem(pnl)
|
||||
end
|
||||
|
||||
function PANEL:OnChildAdded(child)
|
||||
timer.Simple(0, function()
|
||||
if child == self.content or child == self.vbar or child == self.vbar.btnGrip then return end
|
||||
if !IsValid(child) or !IsValid(self) then return end
|
||||
if child:GetParent() == self then
|
||||
child:SetParent(self.content)
|
||||
|
||||
local old = child.OnSizeChanged
|
||||
child.OnSizeChanged = function(...)
|
||||
if old then pcall(old, ...) end
|
||||
if IsValid(self) then
|
||||
self:_markDirty()
|
||||
self:InvalidateLayout(true)
|
||||
self.content:InvalidateLayout(true)
|
||||
self.content:SizeToChildren(false, true)
|
||||
end
|
||||
end
|
||||
|
||||
self:_markDirty()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PANEL:Clear()
|
||||
for _, c in ipairs(self.content:GetChildren()) do c:Remove() end
|
||||
self.offset = 0
|
||||
self.vel = 0
|
||||
self:_markDirty()
|
||||
end
|
||||
|
||||
function PANEL:SetScroll(y)
|
||||
self.offset = y or 0
|
||||
end
|
||||
|
||||
function PANEL:GetScroll()
|
||||
return self.offset
|
||||
end
|
||||
|
||||
function PANEL:_range()
|
||||
if self._needLayout then
|
||||
local w, h = self:GetWide(), self:GetTall()
|
||||
local vbw = self.vbar:GetWide()
|
||||
|
||||
self.content:DockPadding(0, 0, 0, 0)
|
||||
|
||||
local vbReserve = self.vbarReserveWidth or self.vbarExpandedWidth
|
||||
|
||||
self.content:SetPos(self.padL, self.padT - self.offset)
|
||||
|
||||
local contentW = math.max(0, w - self.padL - self.padR - vbReserve - self._vbarPadRight)
|
||||
self.content:SetWide(contentW)
|
||||
self.content:InvalidateLayout(true)
|
||||
self.content:SizeToChildren(false, true)
|
||||
|
||||
local viewH = math.max(0, h - self.padT - self.padB)
|
||||
local contentH = self.content:GetTall()
|
||||
|
||||
if contentH <= viewH then
|
||||
self.vbar:SetVisible(false)
|
||||
self.content:SetWide(math.max(0, w - self.padL - self.padR))
|
||||
self.content:InvalidateLayout(true)
|
||||
self.content:SizeToChildren(false, true)
|
||||
contentH = self.content:GetTall()
|
||||
else
|
||||
self.vbar:SetVisible(true)
|
||||
end
|
||||
|
||||
self._needLayout = false
|
||||
end
|
||||
|
||||
local viewH = math.max(0, self:GetTall() - self.padT - self.padB)
|
||||
local contentH = self.content:GetTall()
|
||||
|
||||
return math.max(0, contentH - viewH), viewH, contentH
|
||||
end
|
||||
|
||||
function PANEL:_nudge(px)
|
||||
self.vel = self.vel + px * 10
|
||||
self.lastInput = CurTime()
|
||||
end
|
||||
|
||||
function PANEL:OnMouseWheeled(delta)
|
||||
local _, _, contentH = self:_range()
|
||||
if contentH <= 0 then return end
|
||||
|
||||
self._springing = false
|
||||
|
||||
self.vel = self.vel - delta * self.scrollStep
|
||||
self.lastInput = CurTime()
|
||||
return true
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(mc)
|
||||
if mc != MOUSE_LEFT then return end
|
||||
|
||||
local hovered = vgui.GetHoveredPanel()
|
||||
if IsValid(hovered) and hovered != self and hovered:IsDescendantOf(self.content) then return end
|
||||
|
||||
self.drag = true
|
||||
self.dragLast = select(2, self:CursorPos())
|
||||
self.vel = 0
|
||||
self.lastInput = CurTime()
|
||||
self:MouseCapture(true)
|
||||
|
||||
self._springing = false
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased(mc)
|
||||
if mc != MOUSE_LEFT then return end
|
||||
self.drag = false
|
||||
self:MouseCapture(false)
|
||||
|
||||
local maxScrollDF = select(1, self:_range()) or 0
|
||||
|
||||
local extraTop = math.max(0, -self.offset)
|
||||
local extraBottom = math.max(0, self.offset - maxScrollDF)
|
||||
|
||||
if extraTop > self.overscrollThreshold then
|
||||
self:_startSpring(0)
|
||||
elseif extraBottom > self.overscrollThreshold then
|
||||
self:_startSpring(maxScrollDF)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnCursorMoved(_, y)
|
||||
if !self.drag then return end
|
||||
|
||||
local dy = y - self.dragLast
|
||||
self.dragLast = y
|
||||
|
||||
local maxScrollDF = self:_range()
|
||||
local next = self.offset - dy
|
||||
if next < 0 then
|
||||
self.offset = self.offset - dy * self.dragRes
|
||||
elseif next > maxScrollDF then
|
||||
self.offset = self.offset - dy * self.dragRes
|
||||
else
|
||||
self.offset = next
|
||||
end
|
||||
|
||||
self.lastInput = CurTime()
|
||||
end
|
||||
|
||||
function PANEL:SetVBarPaddingRight(enabled)
|
||||
if !IsValid(self.vbar) then return end
|
||||
|
||||
self.vbar:DockMargin(enabled and 6 or 0, 0, 0, 0)
|
||||
|
||||
self:_markDirty()
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
self:_markDirty()
|
||||
end
|
||||
|
||||
function PANEL:_startSpring(target)
|
||||
self._springing = true
|
||||
self._springTarget = target
|
||||
self.vel = 0
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
local ft = FrameTime()
|
||||
local maxScrollDF, viewH, contentH = self:_range()
|
||||
|
||||
local extraTop = math.max(0, -self.offset)
|
||||
local extraBottom = math.max(0, self.offset - maxScrollDF)
|
||||
|
||||
if self._springing then
|
||||
if CurTime() - self.lastInput < 0.02 then
|
||||
self._springing = false
|
||||
end
|
||||
end
|
||||
|
||||
if self._springing then
|
||||
local t = math.min(1, ft * self.spring)
|
||||
self.offset = Lerp(t, self.offset, self._springTarget)
|
||||
self.vel = 0
|
||||
if math.abs(self.offset - self._springTarget) < 0.5 then
|
||||
self.offset = self._springTarget
|
||||
self._springing = false
|
||||
end
|
||||
else
|
||||
if !self.drag then
|
||||
self.offset = self.offset + self.vel * ft
|
||||
|
||||
if self.offset < -self.overscroll then
|
||||
self.offset = -self.overscroll
|
||||
self.vel = 0
|
||||
elseif self.offset > maxScrollDF + self.overscroll then
|
||||
self.offset = maxScrollDF + self.overscroll
|
||||
self.vel = 0
|
||||
else
|
||||
self.vel = self.vel * math.max(0, 1 - ft * self.friction)
|
||||
if math.abs(self.vel) < 2 then self.vel = 0 end
|
||||
end
|
||||
|
||||
if CurTime() - self.lastInput > 0.09 and self.vel == 0 then
|
||||
if extraTop > self.overscrollThreshold then
|
||||
self:_startSpring(0)
|
||||
elseif extraBottom > self.overscrollThreshold then
|
||||
self:_startSpring(maxScrollDF)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.content:SetPos(self.padL, self.padT - math.floor(self.offset))
|
||||
|
||||
local vb = self.vbar
|
||||
if !vb:IsVisible() then return end
|
||||
|
||||
local hoveredNow = vb:IsHovered() or vb.btnGrip:IsHovered()
|
||||
if hoveredNow then
|
||||
if vb._hoverEnter == 0 then vb._hoverEnter = CurTime() end
|
||||
vb._hoverExit = 0
|
||||
else
|
||||
if vb._hoverExit == 0 then vb._hoverExit = CurTime() end
|
||||
vb._hoverEnter = 0
|
||||
end
|
||||
|
||||
if vb.Dragging then vb._expanded = true end
|
||||
|
||||
if vb._hoverEnter > 0 and CurTime() - vb._hoverEnter >= self.vbarHoverDelay then
|
||||
vb._expanded = true
|
||||
end
|
||||
if vb._hoverExit > 0 and CurTime() - vb._hoverExit >= self.vbarUnhoverDelay and !vb.Dragging then
|
||||
vb._expanded = false
|
||||
end
|
||||
|
||||
local targetW = (vb._expanded and self.vbarExpandedWidth) or self.vbarDefaultWidth
|
||||
if vb.Dragging then targetW = self.vbarExpandedWidth end
|
||||
|
||||
if self._vb_width == nil then
|
||||
self._vb_width = targetW
|
||||
else
|
||||
self._vb_width = Mantle.func.approachExp(self._vb_width, targetW, self.vbarWidthSpeed, ft)
|
||||
if math.abs(self._vb_width - targetW) < 0.25 then self._vb_width = targetW end
|
||||
end
|
||||
|
||||
local newW = math.max(1, math.floor(self._vb_width))
|
||||
if vb:GetWide() != newW then
|
||||
vb:SetWide(newW)
|
||||
self:_markDirty()
|
||||
end
|
||||
|
||||
local trackH = vb:GetTall()
|
||||
local clampedOffset = math.Clamp(self.offset, 0, maxScrollDF)
|
||||
local ratio = (contentH <= 0) and 1 or math.min(1, viewH / contentH)
|
||||
local gripH = math.max(self.gripMin, math.floor(trackH * ratio))
|
||||
local scroll01 = (maxScrollDF <= 0) and 0 or (clampedOffset / maxScrollDF)
|
||||
|
||||
local topFrac = math.Clamp(extraTop / self.overscroll, 0, 1)
|
||||
local bottomFrac = math.Clamp(extraBottom / self.overscroll, 0, 1)
|
||||
local maxFrac = math.max(topFrac, bottomFrac)
|
||||
local overscrollFrac = math.Clamp(math.max(extraTop, extraBottom) / self.overscroll, 0, 1)
|
||||
|
||||
local gripRatio = gripH / math.max(1, trackH)
|
||||
local weight = math.Clamp((1 - gripRatio) * 1.5, 0, 1)
|
||||
|
||||
local contentToTrack = trackH / math.max(1, contentH)
|
||||
local extraShift = 0
|
||||
if extraTop > 0 then extraShift = -extraTop * contentToTrack
|
||||
elseif extraBottom > 0 then extraShift = extraBottom * contentToTrack end
|
||||
|
||||
local proportionalY = (trackH - gripH) * scroll01
|
||||
|
||||
local desiredY = proportionalY + extraShift * weight * overscrollFrac
|
||||
|
||||
if clampedOffset <= 0.001 then
|
||||
desiredY = 0
|
||||
elseif maxScrollDF > 0 and clampedOffset >= maxScrollDF - 0.001 then
|
||||
desiredY = trackH - gripH
|
||||
end
|
||||
|
||||
local maxShrink = 0.7
|
||||
local visualGripH = gripH * (1 - maxShrink * overscrollFrac * weight)
|
||||
visualGripH = math.max(6, visualGripH)
|
||||
|
||||
local gripSpeed = 14
|
||||
if vb.Dragging then
|
||||
local _, my = vb:CursorPos()
|
||||
local newY = math.Clamp(my - vb._press_off, 0, trackH - visualGripH)
|
||||
local s01 = (trackH - visualGripH) <= 0 and 0 or (newY / (trackH - visualGripH))
|
||||
self.offset = s01 * maxScrollDF
|
||||
self.vel = 0
|
||||
|
||||
self._vb_gripH = visualGripH
|
||||
self._vb_gripY = newY
|
||||
else
|
||||
if self._vb_gripH == nil then
|
||||
self._vb_gripH = visualGripH
|
||||
else
|
||||
self._vb_gripH = Mantle.func.approachExp(self._vb_gripH, visualGripH, gripSpeed, ft)
|
||||
if math.abs(self._vb_gripH - visualGripH) < 0.25 then self._vb_gripH = visualGripH end
|
||||
end
|
||||
|
||||
if self._vb_gripY == nil then
|
||||
self._vb_gripY = desiredY
|
||||
else
|
||||
local speedY = gripSpeed * (1 + maxFrac * 0.5)
|
||||
self._vb_gripY = Mantle.func.approachExp(self._vb_gripY, desiredY, speedY, ft)
|
||||
if math.abs(self._vb_gripY - desiredY) < 0.25 then self._vb_gripY = desiredY end
|
||||
end
|
||||
|
||||
local maxY = math.max(0, trackH - self._vb_gripH)
|
||||
if self._vb_gripY < 0 then self._vb_gripY = 0 end
|
||||
if self._vb_gripY > maxY then self._vb_gripY = maxY end
|
||||
|
||||
if clampedOffset <= 0.001 then
|
||||
self._vb_gripY = 0
|
||||
elseif maxScrollDF > 0 and clampedOffset >= maxScrollDF - 0.001 then
|
||||
self._vb_gripY = trackH - self._vb_gripH
|
||||
end
|
||||
|
||||
if math.abs(self._vb_gripH - visualGripH) < 0.25 then self._vb_gripH = visualGripH end
|
||||
if math.abs((self._vb_gripY or 0) - desiredY) < 0.25 then self._vb_gripY = desiredY end
|
||||
end
|
||||
|
||||
local finalH = math.max(1, math.floor(self._vb_gripH))
|
||||
local finalY = math.floor(math.Clamp(self._vb_gripY or 0, 0, math.max(0, trackH - finalH)))
|
||||
|
||||
if clampedOffset <= 0.001 then finalY = 0 end
|
||||
if maxScrollDF > 0 and clampedOffset >= maxScrollDF - 0.001 then finalY = trackH - finalH end
|
||||
|
||||
vb.btnGrip:SetSize(vb:GetWide(), finalH)
|
||||
vb.btnGrip:SetPos(0, finalY)
|
||||
end
|
||||
|
||||
vgui.Register('MantleScrollPanel', PANEL, 'EditablePanel')
|
||||
@@ -0,0 +1,206 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.text = ''
|
||||
self.min_value = 0
|
||||
self.max_value = 1
|
||||
self.decimals = 0
|
||||
self.convar = nil
|
||||
self.value = 0
|
||||
self.smoothPos = 0
|
||||
self.targetPos = 0
|
||||
self.dragging = false
|
||||
self.hover = false
|
||||
self:SetTall(60)
|
||||
self.OnValueChanged = function() end
|
||||
self._convar_last = nil
|
||||
self._convar_timer = self:CreateConVarSyncTimer()
|
||||
self._dragAlpha = 255
|
||||
end
|
||||
|
||||
function PANEL:CreateConVarSyncTimer()
|
||||
local name = 'MantleSlideBoxSync' .. tostring(self)
|
||||
timer.Create(name, 0.1, 0, function()
|
||||
if not IsValid(self) or not self.convar then return end
|
||||
local cvar = GetConVar(self.convar)
|
||||
if not cvar then return end
|
||||
local val = cvar:GetFloat()
|
||||
if self._convar_last ~= val then
|
||||
self._convar_last = val
|
||||
self:SetValue(val, true)
|
||||
end
|
||||
end)
|
||||
return name
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
if self._convar_timer then
|
||||
timer.Remove(self._convar_timer)
|
||||
self._convar_timer = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetRange(min_value, max_value, decimals)
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value
|
||||
self.decimals = decimals or 0
|
||||
self:SetValue(self.value or min_value)
|
||||
end
|
||||
|
||||
function PANEL:SetConvar(convar)
|
||||
self.convar = convar
|
||||
local cvar = GetConVar(convar)
|
||||
if cvar then
|
||||
self:SetValue(cvar:GetFloat(), true)
|
||||
self._convar_last = cvar:GetFloat()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetText(text)
|
||||
self.text = text
|
||||
end
|
||||
|
||||
function PANEL:SetValue(val, fromConVar)
|
||||
if self.max_value == self.min_value then
|
||||
val = self.min_value
|
||||
else
|
||||
val = math.Clamp(val, self.min_value, self.max_value)
|
||||
end
|
||||
|
||||
if self.decimals > 0 then
|
||||
val = tonumber(string.format('%.' .. tostring(self.decimals) .. 'f', val)) or val
|
||||
else
|
||||
val = math.Round(val)
|
||||
end
|
||||
|
||||
self.value = val
|
||||
local denom = (self.max_value - self.min_value)
|
||||
local progress = denom == 0 and 0 or (val - self.min_value) / denom
|
||||
local w = math.max(0, self:GetWide() - 32)
|
||||
self.targetPos = math.Clamp(w * progress, 0, w)
|
||||
if self.convar and not fromConVar then
|
||||
RunConsoleCommand(self.convar, tostring(val))
|
||||
self._convar_last = val
|
||||
end
|
||||
if self.OnValueChanged then self:OnValueChanged(val) end
|
||||
end
|
||||
|
||||
function PANEL:GetValue()
|
||||
return self.value
|
||||
end
|
||||
|
||||
function PANEL:UpdateSliderByCursorPos(x)
|
||||
local w = math.max(0, self:GetWide() - 32)
|
||||
local progress = math.Clamp(x / w, 0, 1)
|
||||
local new_value = self.min_value + (progress * (self.max_value - self.min_value))
|
||||
if self.decimals > 0 then
|
||||
new_value = tonumber(string.format('%.' .. tostring(self.decimals) .. 'f', new_value))
|
||||
else
|
||||
new_value = math.Round(new_value)
|
||||
end
|
||||
self:SetValue(new_value)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
local ft = FrameTime()
|
||||
local padX = 16
|
||||
local padTop = 2
|
||||
local barY = 32
|
||||
local barH = 6
|
||||
local barR = barH / 2
|
||||
local handleW, handleH = 14, 14
|
||||
local handleR = handleH / 2
|
||||
local textFont = 'Fated.18'
|
||||
local minmaxFont = 'Fated.14'
|
||||
local valueFont = 'Fated.16'
|
||||
local minmaxPadY = 12
|
||||
|
||||
-- Текст сверху
|
||||
draw.SimpleText(self.text, textFont, padX, padTop, Mantle.color.text)
|
||||
|
||||
-- Линия
|
||||
local barStart = padX + handleW / 2
|
||||
local barEnd = w - padX - handleW / 2
|
||||
local barW = math.max(0, barEnd - barStart)
|
||||
|
||||
local denom = (self.max_value - self.min_value)
|
||||
local progress = denom == 0 and 0 or (self.value - self.min_value) / denom
|
||||
progress = math.Clamp(progress, 0, 1)
|
||||
local activeW = barW * progress
|
||||
|
||||
-- Тень под линией
|
||||
if Mantle.ui.convar.depth_ui then
|
||||
RNDX().Rect(barStart, barY, barW, barH)
|
||||
:Rad(barR)
|
||||
:Color(Mantle.color.window_shadow)
|
||||
:Shadow(5, 20)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
-- Фон линии
|
||||
RNDX.Draw(barR, barStart, barY, barW, barH, Mantle.color.focus_panel)
|
||||
RNDX.Draw(barR, barStart, barY, barW, barH, Mantle.color.button_shadow)
|
||||
|
||||
-- Активная линия
|
||||
self.smoothPos = Mantle.func.approachExp(self.smoothPos or 0, activeW, 14, ft)
|
||||
if math.abs(self.smoothPos - activeW) < 0.5 then self.smoothPos = activeW end
|
||||
|
||||
RNDX.Draw(barR, barStart, barY, self.smoothPos, barH, Mantle.color.theme)
|
||||
|
||||
local handleX = barStart + self.smoothPos
|
||||
local handleY = barY + barH / 2
|
||||
|
||||
-- Тень под ручкой
|
||||
RNDX.DrawShadows(handleR, handleX - handleW / 2, handleY - handleH / 2, handleW, handleH, Mantle.color.window_shadow, 3, 10)
|
||||
|
||||
local targetAlpha = self.dragging and 100 or 255
|
||||
self._dragAlpha = Mantle.func.approachExp(self._dragAlpha or 255, targetAlpha, 24, ft)
|
||||
if math.abs(self._dragAlpha - targetAlpha) < 1 then self._dragAlpha = targetAlpha end
|
||||
local colorText = Color(Mantle.color.theme.r, Mantle.color.theme.g, Mantle.color.theme.b, math.floor(self._dragAlpha))
|
||||
|
||||
-- Ручка
|
||||
RNDX.Draw(handleR, handleX - handleW / 2, handleY - handleH / 2, handleW, handleH, colorText)
|
||||
|
||||
-- Значение справа от линии
|
||||
local valText = (self.decimals > 0) and tostring(self.value) or tostring(self.value)
|
||||
draw.SimpleText(valText, valueFont, barEnd + handleW / 2 + 4, barY + barH / 2, colorText, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
-- min/max под линией
|
||||
draw.SimpleText(self.min_value, minmaxFont, barStart, barY + barH + minmaxPadY - 4, Mantle.color.gray, TEXT_ALIGN_LEFT)
|
||||
draw.SimpleText(self.max_value, minmaxFont, barEnd, barY + barH + minmaxPadY - 4, Mantle.color.gray, TEXT_ALIGN_RIGHT)
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
local x = self:CursorPos()
|
||||
self:UpdateSliderByCursorPos(x)
|
||||
self.dragging = true
|
||||
self:MouseCapture(true)
|
||||
self.ripple_x = x
|
||||
self.ripple_anim = 0
|
||||
self.ripple_active = true
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased(mcode)
|
||||
if mcode == MOUSE_LEFT then
|
||||
self.dragging = false
|
||||
self:MouseCapture(false)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnCursorMoved(x)
|
||||
if self.dragging then
|
||||
self:UpdateSliderByCursorPos(x)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnCursorEntered()
|
||||
self.hover = true
|
||||
end
|
||||
|
||||
function PANEL:OnCursorExited()
|
||||
self.hover = false
|
||||
end
|
||||
|
||||
vgui.Register('MantleSlideBox', PANEL, 'Panel')
|
||||
@@ -0,0 +1,513 @@
|
||||
local PANEL = {}
|
||||
|
||||
local FrameTime = FrameTime
|
||||
local CurTime = CurTime
|
||||
local Lerp = Lerp
|
||||
local math_max = math.max
|
||||
local math_floor = math.floor
|
||||
|
||||
function PANEL:Init()
|
||||
self.columns = {}
|
||||
self.rows = {}
|
||||
self.headerHeight = 36
|
||||
self.rowHeight = 32
|
||||
self.font = 'Fated.18'
|
||||
self.rowFont = 'Fated.16'
|
||||
self.selectedRow = nil
|
||||
self.sortColumn = nil
|
||||
self.sortDesc = true
|
||||
self.sortState = 0
|
||||
self._originalRows = nil
|
||||
self.hoverAnim = 0
|
||||
self.padding = 8
|
||||
|
||||
self.sidePadding = 12
|
||||
self.vbarRightPadding = 6
|
||||
self.vbarLeftExtra = 0
|
||||
|
||||
self.header = vgui.Create('Panel', self)
|
||||
self.header:Dock(TOP)
|
||||
self.header:SetTall(self.headerHeight)
|
||||
|
||||
self.scrollPanel = vgui.Create('MantleScrollPanel', self)
|
||||
self.scrollPanel:Dock(FILL)
|
||||
self.scrollPanel:DisableVBarPadding()
|
||||
|
||||
self.content = vgui.Create('Panel', self.scrollPanel)
|
||||
self.content:Dock(TOP)
|
||||
self.content.Paint = nil
|
||||
|
||||
self._rowPanels = {}
|
||||
self._headerButtons = {}
|
||||
self._colWidthsTarget = {}
|
||||
self._colWidthsCurrent = {}
|
||||
self._lastVBarVis = nil
|
||||
self._headerPrevActive = {}
|
||||
|
||||
self.OnAction = function() end
|
||||
self.OnRightClick = function() end
|
||||
|
||||
self.Think = function()
|
||||
local dt = FrameTime()
|
||||
|
||||
self:UpdateColumnWidthTargets()
|
||||
|
||||
for i = 1, #self.columns do
|
||||
local tgt = self._colWidthsTarget[i] or (self.columns[i] and self.columns[i].width or 100)
|
||||
self._colWidthsCurrent[i] = Mantle.func.approachExp(self._colWidthsCurrent[i] or tgt, tgt, 20, dt)
|
||||
end
|
||||
|
||||
local leftPad = self.sidePadding + ((self._lastVBarVis and self.vbarLeftExtra) or 0)
|
||||
local x = leftPad
|
||||
for i, btn in ipairs(self._headerButtons) do
|
||||
local w = math_floor(self._colWidthsCurrent[i] or (self.columns[i] and self.columns[i].width or 100))
|
||||
if IsValid(btn) then
|
||||
btn:SetSize(w, self.headerHeight)
|
||||
btn:SetPos(x, 0)
|
||||
end
|
||||
x = x + w
|
||||
end
|
||||
|
||||
local total = 0
|
||||
for i = 1, #self.columns do total = total + (self._colWidthsCurrent[i] or self.columns[i].width) end
|
||||
|
||||
local panelW = self:GetWide() or 0
|
||||
if panelW <= 0 then
|
||||
local par = self:GetParent()
|
||||
if IsValid(par) and par.GetWide then panelW = par:GetWide() end
|
||||
end
|
||||
if panelW <= 0 then panelW = ScrW() end
|
||||
|
||||
local rightPad = self.sidePadding + ((self._lastVBarVis and self.vbarRightPadding) or 0)
|
||||
local contentW = math_max(total + leftPad + rightPad, panelW)
|
||||
|
||||
for _, row in ipairs(self._rowPanels) do
|
||||
if IsValid(row) and row._labels then
|
||||
local x2 = leftPad
|
||||
local hv = row._hoverAlpha or 0
|
||||
local eased = Mantle.func.easeOutCubic(math.Clamp(hv, 0, 1))
|
||||
local shift = math_floor(6 * eased)
|
||||
for i, label in ipairs(row._labels) do
|
||||
local w = math_floor(self._colWidthsCurrent[i] or (self.columns[i] and self.columns[i].width or 100))
|
||||
if IsValid(label) then
|
||||
local dx = 0
|
||||
if i == 1 then
|
||||
dx = shift
|
||||
elseif i == #self.columns then
|
||||
dx = -shift
|
||||
end
|
||||
label:SetSize(w, self.rowHeight)
|
||||
label:SetPos(x2 + dx, 0)
|
||||
end
|
||||
x2 = x2 + w
|
||||
end
|
||||
row:SetWide(contentW)
|
||||
end
|
||||
end
|
||||
|
||||
self.content:SetWide(contentW)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:AddColumn(name, width, align, sortable)
|
||||
table.insert(self.columns, {
|
||||
name = name,
|
||||
width = width or 100,
|
||||
align = align or TEXT_ALIGN_LEFT,
|
||||
sortable = sortable or false
|
||||
})
|
||||
end
|
||||
|
||||
function PANEL:AddItem(...)
|
||||
local args = {...}
|
||||
if #args != #self.columns then
|
||||
print(Mantle.lang.get('mantle', 'table_wrong_args'))
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(self.rows, args)
|
||||
self:RebuildRows()
|
||||
return #self.rows
|
||||
end
|
||||
|
||||
local function getValueType(value)
|
||||
if value == nil then return 'nil' end
|
||||
value = tostring(value)
|
||||
return tonumber(value) and 'number' or 'string'
|
||||
end
|
||||
|
||||
local function compareValues(a, b)
|
||||
if a == nil and b == nil then return false end
|
||||
if a == nil then return true end
|
||||
if b == nil then return false end
|
||||
|
||||
local typeA = getValueType(a)
|
||||
local typeB = getValueType(b)
|
||||
|
||||
if typeA != typeB then
|
||||
return typeA < typeB
|
||||
end
|
||||
|
||||
if typeA == 'number' then
|
||||
local numA = tonumber(a) or 0
|
||||
local numB = tonumber(b) or 0
|
||||
return numA > numB
|
||||
else
|
||||
local strA = tostring(a)
|
||||
local strB = tostring(b)
|
||||
return strA < strB
|
||||
end
|
||||
end
|
||||
|
||||
local function cloneRows(tbl)
|
||||
local out = {}
|
||||
for i, v in ipairs(tbl) do out[i] = v end
|
||||
return out
|
||||
end
|
||||
|
||||
function PANEL:SortByColumn(columnIndex)
|
||||
local column = self.columns[columnIndex]
|
||||
if !column or !column.sortable then return end
|
||||
|
||||
if self.sortColumn != columnIndex then
|
||||
self.sortColumn = columnIndex
|
||||
|
||||
local numCount, total = 0, 0
|
||||
for _, row in ipairs(self.rows) do
|
||||
local v = row[columnIndex]
|
||||
if v != nil then
|
||||
total = total + 1
|
||||
if tonumber(tostring(v)) then numCount = numCount + 1 end
|
||||
end
|
||||
end
|
||||
|
||||
local isNumeric = (total > 0 and numCount >= math.ceil(total / 2))
|
||||
self.sortDesc = isNumeric
|
||||
else
|
||||
self.sortDesc = !self.sortDesc
|
||||
end
|
||||
|
||||
local desc = self.sortDesc
|
||||
|
||||
table.sort(self.rows, function(a, b)
|
||||
local va = a[columnIndex]
|
||||
local vb = b[columnIndex]
|
||||
|
||||
if va == nil and vb == nil then return false end
|
||||
if va == nil then return !desc end
|
||||
if vb == nil then return desc end
|
||||
|
||||
local sa = tostring(va)
|
||||
local sb = tostring(vb)
|
||||
|
||||
local na = tonumber(sa)
|
||||
local nb = tonumber(sb)
|
||||
|
||||
if na and nb then
|
||||
if desc then
|
||||
return na > nb
|
||||
else
|
||||
return na < nb
|
||||
end
|
||||
end
|
||||
|
||||
if na and !nb then
|
||||
return desc
|
||||
elseif nb and !na then
|
||||
return !desc
|
||||
end
|
||||
|
||||
local la = string.lower(sa)
|
||||
local lb = string.lower(sb)
|
||||
if desc then
|
||||
return la > lb
|
||||
else
|
||||
return la < lb
|
||||
end
|
||||
end)
|
||||
|
||||
self:RebuildRows()
|
||||
end
|
||||
|
||||
function PANEL:UpdateColumnWidthTargets()
|
||||
local cols = self.columns
|
||||
local n = #cols
|
||||
if n == 0 then return end
|
||||
|
||||
local panelW = self:GetWide() or 0
|
||||
if (!panelW) or panelW <= 0 then
|
||||
local parent = self:GetParent()
|
||||
if IsValid(parent) and parent.GetWide then panelW = parent:GetWide() end
|
||||
end
|
||||
if (!panelW) or panelW <= 0 then panelW = ScrW() end
|
||||
|
||||
local vbar = (IsValid(self.scrollPanel) and self.scrollPanel.GetVBar) and self.scrollPanel:GetVBar() or nil
|
||||
local vbarVisible = (IsValid(vbar) and vbar:IsVisible())
|
||||
local vbarW = (vbarVisible and vbar:GetWide() or 0)
|
||||
|
||||
local leftPad = self.sidePadding + (vbarVisible and self.vbarLeftExtra or 0)
|
||||
local rightPad = self.sidePadding + (vbarVisible and self.vbarRightPadding or 0)
|
||||
|
||||
local usable = math_max(0, panelW - leftPad - rightPad - vbarW)
|
||||
|
||||
local used = 0
|
||||
for i = 1, math.max(0, n - 1) do
|
||||
self._colWidthsTarget[i] = cols[i].width or 100
|
||||
used = used + self._colWidthsTarget[i]
|
||||
end
|
||||
|
||||
local lastMin = cols[n].width or 100
|
||||
local remaining = usable - used
|
||||
if remaining < lastMin then remaining = lastMin end
|
||||
self._colWidthsTarget[n] = remaining
|
||||
|
||||
for i = 1, n do
|
||||
if self._colWidthsCurrent[i] == nil then
|
||||
self._colWidthsCurrent[i] = self._colWidthsTarget[i]
|
||||
end
|
||||
end
|
||||
|
||||
self._lastVBarVis = vbarVisible
|
||||
end
|
||||
|
||||
function PANEL:CreateHeader()
|
||||
local prev = {}
|
||||
for i, btn in ipairs(self._headerButtons) do
|
||||
if IsValid(btn) then prev[i] = btn._activeAlpha or 0 end
|
||||
end
|
||||
|
||||
self.header:Clear()
|
||||
self._headerButtons = {}
|
||||
|
||||
self.header.Paint = function(_, w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Radii(16, 16, 0, 0)
|
||||
:Color(Mantle.color.focus_panel)
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
self:UpdateColumnWidthTargets()
|
||||
|
||||
local xPos = self.sidePadding + ((self._lastVBarVis and self.vbarLeftExtra) or 0)
|
||||
for i, column in ipairs(self.columns) do
|
||||
local w = math_floor(self._colWidthsCurrent[i] or self._colWidthsTarget[i] or column.width)
|
||||
local label = vgui.Create('DButton', self.header)
|
||||
label:SetText('')
|
||||
label:SetSize(w, self.headerHeight)
|
||||
label:SetPos(xPos, 0)
|
||||
label._hover = 0
|
||||
label._activeAlpha = prev[i] or ((self.sortColumn == i) and 1 or 0)
|
||||
|
||||
label.Paint = function(s, bw, bh)
|
||||
local dt = FrameTime()
|
||||
local target = s:IsHovered() and 1 or 0
|
||||
s._hover = Mantle.func.approachExp(s._hover or 0, target, 14, dt)
|
||||
local activeTarget = (self.sortColumn == i) and 1 or 0
|
||||
s._activeAlpha = Mantle.func.approachExp(s._activeAlpha or 0, activeTarget, 12, dt)
|
||||
|
||||
local tr = Lerp(s._activeAlpha, Mantle.color.text.r, Mantle.color.theme.r)
|
||||
local tg = Lerp(s._activeAlpha, Mantle.color.text.g, Mantle.color.theme.g)
|
||||
local tb = Lerp(s._activeAlpha, Mantle.color.text.b, Mantle.color.theme.b)
|
||||
local ta = Lerp(s._activeAlpha, Mantle.color.text.a, Mantle.color.theme.a)
|
||||
local textColor = Color(math_floor(tr), math_floor(tg), math_floor(tb), math_floor(ta))
|
||||
|
||||
draw.SimpleText(column.name, self.font, bw/2, bh/2, textColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
if column.sortable then
|
||||
label.DoClick = function()
|
||||
self:SortByColumn(i)
|
||||
Mantle.func.sound()
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(self._headerButtons, label)
|
||||
xPos = xPos + w
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateRow(rowIndex, rowData)
|
||||
local row = vgui.Create('DButton', self.content)
|
||||
row:Dock(TOP)
|
||||
row:DockMargin(0, 0, 0, 1)
|
||||
row:SetTall(self.rowHeight)
|
||||
row:SetText('')
|
||||
|
||||
row._index = rowIndex
|
||||
row._hoverAlpha = 0
|
||||
row._selectedAlpha = 0
|
||||
row._labels = {}
|
||||
|
||||
row.Paint = function(s, w, h)
|
||||
local dt = FrameTime()
|
||||
local hoverTarget = s:IsHovered() and 1 or 0
|
||||
s._hoverAlpha = Mantle.func.approachExp(s._hoverAlpha, hoverTarget, 18, dt)
|
||||
local selTarget = (self.selectedRow == s._index) and 1 or 0
|
||||
s._selectedAlpha = Mantle.func.approachExp(s._selectedAlpha, selTarget, 22, dt)
|
||||
|
||||
local base = Mantle.color.panel_alpha[1]
|
||||
local hoverCol = Mantle.color.hover
|
||||
local selCol = Mantle.color.theme
|
||||
|
||||
local mixHover = s._hoverAlpha * (1 - s._selectedAlpha)
|
||||
local blendA = s._selectedAlpha * 0.9 + mixHover * 0.35
|
||||
|
||||
local r = Lerp(blendA, base.r, selCol.r)
|
||||
local g = Lerp(blendA, base.g, selCol.g)
|
||||
local b = Lerp(blendA, base.b, selCol.b)
|
||||
local a = Lerp(blendA, base.a, selCol.a)
|
||||
|
||||
if s._hoverAlpha > 0.01 and s._selectedAlpha < 0.9 then
|
||||
local hoverR = Lerp(s._hoverAlpha * 0.6, r, hoverCol.r)
|
||||
local hoverG = Lerp(s._hoverAlpha * 0.6, g, hoverCol.g)
|
||||
local hoverB = Lerp(s._hoverAlpha * 0.6, b, hoverCol.b)
|
||||
r,g,b = hoverR, hoverG, hoverB
|
||||
end
|
||||
|
||||
RNDX().Rect(0, 0, w, math.max(0, h - 1))
|
||||
:Color(Color(math.floor(r), math.floor(g), math.floor(b), math.floor(a)))
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
row.DoClick = function()
|
||||
self.selectedRow = rowIndex
|
||||
self._keyboardIndex = rowIndex
|
||||
self.OnAction(rowData)
|
||||
Mantle.func.sound()
|
||||
end
|
||||
|
||||
row.DoRightClick = function()
|
||||
self.selectedRow = rowIndex
|
||||
self.OnRightClick(rowData)
|
||||
local menu = Mantle.ui.derma_menu()
|
||||
for i, column in ipairs(self.columns) do
|
||||
menu:AddOption(Mantle.lang.get('mantle', 'table_copy') .. ' ' .. column.name, function()
|
||||
SetClipboardText(tostring(rowData[i]))
|
||||
end)
|
||||
end
|
||||
menu:AddSpacer()
|
||||
menu:AddOption(Mantle.lang.get('mantle', 'table_delete_row'), function()
|
||||
self:RemoveRow(rowIndex)
|
||||
end, 'icon16/delete.png')
|
||||
end
|
||||
|
||||
local leftPad = self.sidePadding + ((self._lastVBarVis and self.vbarLeftExtra) or 0)
|
||||
local xPos = leftPad
|
||||
for i, column in ipairs(self.columns) do
|
||||
local w = math_floor(self._colWidthsCurrent[i] or self._colWidthsTarget[i] or column.width)
|
||||
local label = vgui.Create('DLabel', row)
|
||||
label:SetText(tostring(rowData[i]))
|
||||
label:SetFont(self.rowFont)
|
||||
label:SetTextColor(Mantle.color.text)
|
||||
label:SetSize(w, self.rowHeight)
|
||||
label:SetPos(xPos, 0)
|
||||
|
||||
if column.align == TEXT_ALIGN_LEFT then
|
||||
label:SetTextInset(self.padding, 0)
|
||||
label:SetContentAlignment(4)
|
||||
elseif column.align == TEXT_ALIGN_RIGHT then
|
||||
label:SetTextInset(0, 0)
|
||||
label:SetContentAlignment(6)
|
||||
else
|
||||
label:SetTextInset(0, 0)
|
||||
label:SetContentAlignment(5)
|
||||
end
|
||||
|
||||
table.insert(row._labels, label)
|
||||
xPos = xPos + w
|
||||
end
|
||||
|
||||
table.insert(self._rowPanels, row)
|
||||
end
|
||||
|
||||
function PANEL:RebuildRows()
|
||||
local savedRowHover = {}
|
||||
for idx, oldRow in ipairs(self._rowPanels) do
|
||||
if IsValid(oldRow) then
|
||||
savedRowHover[idx] = oldRow._hoverAlpha or 0
|
||||
end
|
||||
end
|
||||
|
||||
local prevHeader = {}
|
||||
for i, btn in ipairs(self._headerButtons) do
|
||||
if IsValid(btn) then prevHeader[i] = btn._activeAlpha or 0 end
|
||||
end
|
||||
|
||||
self.content:Clear()
|
||||
self._rowPanels = {}
|
||||
self._headerButtons = {}
|
||||
|
||||
self:UpdateColumnWidthTargets()
|
||||
|
||||
self:CreateHeader()
|
||||
|
||||
for rowIndex, rowData in ipairs(self.rows) do
|
||||
self:CreateRow(rowIndex, rowData)
|
||||
if savedRowHover[rowIndex] and IsValid(self._rowPanels[#self._rowPanels]) then
|
||||
self._rowPanels[#self._rowPanels]._hoverAlpha = savedRowHover[rowIndex]
|
||||
end
|
||||
end
|
||||
|
||||
local total = 0
|
||||
for i = 1, #self.columns do total = total + (self._colWidthsTarget[i] or self.columns[i].width) end
|
||||
|
||||
local panelW = self:GetWide() or 0
|
||||
if panelW <= 0 then
|
||||
local parent = self:GetParent()
|
||||
if IsValid(parent) and parent.GetWide then panelW = parent:GetWide() end
|
||||
end
|
||||
if panelW <= 0 then panelW = ScrW() end
|
||||
|
||||
local leftPad = self.sidePadding + ((self._lastVBarVis and self.vbarLeftExtra) or 0)
|
||||
local rightPad = self.sidePadding + ((self._lastVBarVis and self.vbarRightPadding) or 0)
|
||||
local contentW = math_max(total + leftPad + rightPad, panelW)
|
||||
|
||||
self.content:SetSize(contentW, #self.rows * (self.rowHeight + 1))
|
||||
self.scrollPanel:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:SetAction(func)
|
||||
self.OnAction = func
|
||||
end
|
||||
function PANEL:SetRightClickAction(func)
|
||||
self.OnRightClick = func
|
||||
end
|
||||
|
||||
function PANEL:Clear()
|
||||
self.rows = {}
|
||||
self.selectedRow = nil
|
||||
self.content:Clear()
|
||||
end
|
||||
|
||||
function PANEL:GetSelectedRow()
|
||||
return self.selectedRow and self.rows[self.selectedRow] or nil
|
||||
end
|
||||
|
||||
function PANEL:GetRowCount()
|
||||
return #self.rows
|
||||
end
|
||||
|
||||
function PANEL:RemoveRow(index)
|
||||
if index and index > 0 and index <= #self.rows then
|
||||
table.remove(self.rows, index)
|
||||
if self.selectedRow == index then
|
||||
self.selectedRow = nil
|
||||
elseif self.selectedRow and self.selectedRow > index then
|
||||
self.selectedRow = self.selectedRow - 1
|
||||
end
|
||||
self:RebuildRows()
|
||||
self.scrollPanel:InvalidateLayout(true)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
RNDX().Rect(0, 0, w, h)
|
||||
:Rad(16)
|
||||
:Color(Mantle.color.panel_alpha[2])
|
||||
:Shape(RNDX.SHAPE_IOS)
|
||||
:Draw()
|
||||
end
|
||||
|
||||
vgui.Register('MantleTable', PANEL, 'Panel')
|
||||
@@ -0,0 +1,196 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.tabs = {}
|
||||
self.active_id = 1
|
||||
self.tab_height = 38
|
||||
self.animation_speed = 12
|
||||
self.tab_style = 'modern' -- modern или classic
|
||||
self.indicator_height = 2
|
||||
|
||||
self.indicator_x = 0
|
||||
self.indicator_w = 0
|
||||
self.indicator_target_x = 0
|
||||
self.indicator_target_w = 0
|
||||
|
||||
self.panel_tabs = vgui.Create('Panel', self)
|
||||
self.panel_tabs.Paint = nil
|
||||
|
||||
self.content = vgui.Create('Panel', self)
|
||||
self.content.Paint = nil
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
if self.tab_style == 'modern' then
|
||||
self.indicator_x = Mantle.func.approachExp(self.indicator_x, self.indicator_target_x, self.animation_speed, FrameTime())
|
||||
self.indicator_w = Mantle.func.approachExp(self.indicator_w, self.indicator_target_w, self.animation_speed, FrameTime())
|
||||
if math.abs(self.indicator_x - self.indicator_target_x) < 0.5 then
|
||||
self.indicator_x = self.indicator_target_x
|
||||
end
|
||||
if math.abs(self.indicator_w - self.indicator_target_w) < 0.5 then
|
||||
self.indicator_w = self.indicator_target_w
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetTabStyle(style)
|
||||
self.tab_style = style
|
||||
self:Rebuild()
|
||||
end
|
||||
|
||||
function PANEL:SetTabHeight(height)
|
||||
self.tab_height = height
|
||||
self:Rebuild()
|
||||
end
|
||||
|
||||
function PANEL:SetIndicatorHeight(height)
|
||||
self.indicator_height = height
|
||||
self:Rebuild()
|
||||
end
|
||||
|
||||
function PANEL:AddTab(name, pan, icon)
|
||||
local newId = #self.tabs + 1
|
||||
|
||||
self.tabs[newId] = {
|
||||
name = name,
|
||||
pan = pan,
|
||||
icon = icon
|
||||
}
|
||||
|
||||
self.tabs[newId].pan:SetParent(self.content)
|
||||
self.tabs[newId].pan:Dock(FILL)
|
||||
self.tabs[newId].pan:SetVisible(newId == 1 and true or false)
|
||||
|
||||
self:Rebuild()
|
||||
end
|
||||
|
||||
local color_btn_hovered = Color(255, 255, 255, 10)
|
||||
|
||||
function PANEL:Rebuild()
|
||||
self.panel_tabs:Clear()
|
||||
|
||||
for id, tab in ipairs(self.tabs) do
|
||||
local btnTab = vgui.Create('Button', self.panel_tabs)
|
||||
tab._btn = btnTab
|
||||
if self.tab_style == 'modern' then
|
||||
surface.SetFont('Fated.18')
|
||||
local textW = select(1, surface.GetTextSize(tab.name))
|
||||
local iconW = tab.icon and 16 or 0
|
||||
local iconTextGap = tab.icon and 8 or 0
|
||||
local padding = 16
|
||||
local btnWidth = padding + iconW + iconTextGap + textW + padding
|
||||
btnTab:Dock(LEFT)
|
||||
btnTab:DockMargin(0, 0, 6, 0)
|
||||
btnTab:SetTall(34)
|
||||
btnTab:SetWide(btnWidth)
|
||||
else
|
||||
btnTab:Dock(TOP)
|
||||
btnTab:DockMargin(0, 0, 0, 6)
|
||||
btnTab:SetTall(34)
|
||||
end
|
||||
|
||||
btnTab:SetText('')
|
||||
btnTab.DoClick = function()
|
||||
self.tabs[self.active_id].pan:SetVisible(false)
|
||||
tab.pan:SetVisible(true)
|
||||
self.active_id = id
|
||||
if self.tab_style == 'modern' and tab._btn then
|
||||
self.indicator_target_x = tab._btn:GetX()
|
||||
self.indicator_target_w = tab._btn:GetWide()
|
||||
end
|
||||
Mantle.func.sound()
|
||||
end
|
||||
btnTab.DoRightClick = function()
|
||||
local dm = Mantle.ui.derma_menu()
|
||||
for k, tab in pairs(self.tabs) do
|
||||
dm:AddOption(tab.name, function()
|
||||
self.tabs[self.active_id].pan:SetVisible(false)
|
||||
tab.pan:SetVisible(true)
|
||||
self.active_id = k
|
||||
if self.tab_style == 'modern' and tab._btn then
|
||||
self.indicator_target_x = tab._btn:GetX()
|
||||
self.indicator_target_w = tab._btn:GetWide()
|
||||
end
|
||||
end, tab.icon)
|
||||
end
|
||||
end
|
||||
|
||||
btnTab.Paint = function(s, w, h)
|
||||
local isActive = self.active_id == id
|
||||
local colorText = isActive and Mantle.color.theme or Mantle.color.text
|
||||
local colorIcon = isActive and Mantle.color.theme or color_white
|
||||
|
||||
if self.tab_style == 'modern' then
|
||||
if s:IsHovered() then
|
||||
RNDX.Draw(16, 0, 0, w, h, color_btn_hovered, RNDX.SHAPE_IOS + (isActive and RNDX.NO_BL + RNDX.NO_BR or 0))
|
||||
end
|
||||
|
||||
local padding = 16
|
||||
local iconW = tab.icon and 16 or 0
|
||||
local iconTextGap = tab.icon and 8 or 0
|
||||
local textX = padding + (iconW > 0 and (iconW + iconTextGap) or 0)
|
||||
|
||||
if tab.icon then
|
||||
RNDX.DrawMaterial(0, padding, (h - 16) * 0.5, 16, 16, colorIcon, tab.icon)
|
||||
end
|
||||
|
||||
draw.SimpleText(tab.name, 'Fated.18', textX, h * 0.5, colorText, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
else
|
||||
if s:IsHovered() then
|
||||
RNDX.Draw(24, 0, 0, w, h, color_btn_hovered, RNDX.SHAPE_IOS)
|
||||
end
|
||||
|
||||
draw.SimpleText(tab.name, 'Fated.18', 34, h * 0.5 - 1, colorText, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
if tab.icon then
|
||||
RNDX.DrawMaterial(0, 9, 9, 16, 16, colorIcon, tab.icon)
|
||||
else
|
||||
RNDX.Draw(24, 9, 9, 16, 16, colorIcon, RNDX.SHAPE_IOS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.panel_tabs.Paint = function(s, w, h)
|
||||
if self.tab_style == 'modern' and self.indicator_w > 0 then
|
||||
RNDX.Draw(0, self.indicator_x, h - self.indicator_height, self.indicator_w, self.indicator_height, Mantle.color.theme)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
if self.tab_style == 'modern' then
|
||||
self.panel_tabs:Dock(TOP)
|
||||
self.panel_tabs:DockMargin(0, 0, 0, 4)
|
||||
self.panel_tabs:SetTall(self.tab_height)
|
||||
else
|
||||
self.panel_tabs:Dock(LEFT)
|
||||
self.panel_tabs:DockMargin(0, 0, 4, 0)
|
||||
self.panel_tabs:SetWide(190)
|
||||
end
|
||||
|
||||
self.content:Dock(FILL)
|
||||
|
||||
if self.tab_style == 'modern' then
|
||||
local activeBtn = nil
|
||||
if self.tabs[self.active_id] then
|
||||
activeBtn = self.tabs[self.active_id]._btn
|
||||
end
|
||||
|
||||
if IsValid(activeBtn) then
|
||||
local bx, by = activeBtn:GetPos()
|
||||
local bw, bh = activeBtn:GetSize()
|
||||
self.indicator_target_x = bx
|
||||
self.indicator_target_w = bw
|
||||
if self.indicator_w == 0 and self.indicator_x == 0 then
|
||||
self.indicator_x = self.indicator_target_x
|
||||
self.indicator_w = self.indicator_target_w
|
||||
end
|
||||
else
|
||||
self.indicator_target_x = 0
|
||||
self.indicator_target_w = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register('MantleTabs', PANEL, 'Panel')
|
||||
@@ -0,0 +1,257 @@
|
||||
local PANEL = {}
|
||||
|
||||
local function utf8_iter(s)
|
||||
return s:gmatch('([%z\1-\127\194-\244][\128-\191]*)')
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
self.text = ''
|
||||
self.font = 'Fated.18'
|
||||
self.color = Mantle.color.text
|
||||
self.align = TEXT_ALIGN_LEFT
|
||||
self.valign = 'top'
|
||||
self.padding = 6
|
||||
|
||||
self._lines = {''}
|
||||
self._line_h = 16
|
||||
self._last_w, self._last_h = 0, 0
|
||||
|
||||
self:SetMouseInputEnabled(false)
|
||||
self:SetKeyboardInputEnabled(false)
|
||||
end
|
||||
|
||||
function PANEL:SetText(text)
|
||||
self.text = text
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:GetText()
|
||||
return self.text
|
||||
end
|
||||
|
||||
function PANEL:SetFont(font)
|
||||
self.font = font
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:SetColor(col)
|
||||
self.color = col
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:SetAlign(a)
|
||||
self.align = a
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL:SetVAlign(v)
|
||||
if v == 'top' or v == 'center' or v == 'bottom' then
|
||||
self.valign = v
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetPadding(p)
|
||||
self.padding = p
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
local function GetTextSize(font, txt)
|
||||
surface.SetFont(font)
|
||||
local ok, w, h = pcall(surface.GetTextSize, txt)
|
||||
if not ok then return 0, 16 end
|
||||
if not h or type(h) != 'number' or h <= 0 then
|
||||
local ok2, _, h2 = pcall(surface.GetTextSize, 'Ay')
|
||||
if ok2 and type(h2) == 'number' and h2 > 0 then
|
||||
h = h2
|
||||
else
|
||||
h = 16
|
||||
end
|
||||
end
|
||||
return tonumber(w) or 0, h
|
||||
end
|
||||
|
||||
local function WrapAndEllipsize(text, font, maxw, max_lines)
|
||||
if maxw <= 0 or max_lines <= 0 then
|
||||
return {''}, true
|
||||
end
|
||||
|
||||
local ell = '...'
|
||||
local ell_w = GetTextSize(font, ell)
|
||||
local paragraphs = string.Explode('\n', text)
|
||||
local lines = {}
|
||||
local truncated = false
|
||||
|
||||
for pi = 1, #paragraphs do
|
||||
local para = paragraphs[pi]
|
||||
|
||||
if para == '' then
|
||||
table.insert(lines, '')
|
||||
if #lines >= max_lines then
|
||||
truncated = (pi < #paragraphs)
|
||||
break
|
||||
end
|
||||
else
|
||||
local words = string.Explode(' ', para)
|
||||
local i = 1
|
||||
local cur = ''
|
||||
|
||||
while i <= #words do
|
||||
local w = words[i]
|
||||
local test = (cur == '') and w or (cur .. ' ' .. w)
|
||||
local tw = GetTextSize(font, test)
|
||||
if tw <= maxw then
|
||||
cur = test
|
||||
i = i + 1
|
||||
else
|
||||
if cur != '' then
|
||||
table.insert(lines, cur)
|
||||
cur = ''
|
||||
if #lines >= max_lines then break end
|
||||
else
|
||||
local part = ''
|
||||
|
||||
for ch in utf8_iter(w) do
|
||||
local tpart = part .. ch
|
||||
local tw2 = GetTextSize(font, tpart)
|
||||
if tw2 <= maxw then
|
||||
part = tpart
|
||||
else
|
||||
if part == '' then
|
||||
local ch_w = GetTextSize(font, ch)
|
||||
if ch_w <= maxw then
|
||||
table.insert(lines, ch)
|
||||
else
|
||||
table.insert(lines, ch)
|
||||
end
|
||||
else
|
||||
table.insert(lines, part)
|
||||
local ch_w = GetTextSize(font, ch)
|
||||
if ch_w <= maxw then
|
||||
part = ch
|
||||
else
|
||||
table.insert(lines, ch)
|
||||
part = ''
|
||||
end
|
||||
end
|
||||
part = part or ''
|
||||
|
||||
if #lines >= max_lines then break end
|
||||
end
|
||||
end
|
||||
if #lines >= max_lines then break end
|
||||
cur = part
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #lines < max_lines and cur != '' then
|
||||
table.insert(lines, cur)
|
||||
end
|
||||
|
||||
if #lines >= max_lines then
|
||||
if i <= #words or pi < #paragraphs then
|
||||
truncated = true
|
||||
end
|
||||
|
||||
if truncated then
|
||||
local rest = ''
|
||||
if i <= #words then
|
||||
for j = i, #words do
|
||||
rest = rest .. words[j] .. (j < #words and ' ' or '')
|
||||
end
|
||||
end
|
||||
if pi < #paragraphs then
|
||||
for pj = pi + 1, #paragraphs do
|
||||
if paragraphs[pj] != '' then
|
||||
rest = rest .. (rest != '' and ' ' or '') .. paragraphs[pj]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #lines == 0 then
|
||||
lines[1] = ''
|
||||
end
|
||||
|
||||
local lastIdx = #lines
|
||||
local prev = lines[lastIdx] or ''
|
||||
if rest == '' then
|
||||
rest = prev
|
||||
else
|
||||
rest = prev .. (rest != '' and (' ' .. rest) or '')
|
||||
end
|
||||
|
||||
local res = ''
|
||||
for ch in utf8_iter(rest) do
|
||||
local t = res .. ch
|
||||
local tw = GetTextSize(font, t)
|
||||
if tw + ell_w <= maxw then
|
||||
res = t
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
lines[lastIdx] = (res == '' and ell) or (res .. ell)
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #lines == 0 then lines[1] = '' end
|
||||
return lines, truncated
|
||||
end
|
||||
|
||||
function PANEL:_rebuild_if_needed()
|
||||
local w, h = self:GetSize()
|
||||
if w == self._last_w and h == self._last_h then return end
|
||||
self._last_w, self._last_h = w, h
|
||||
|
||||
local avail_w_df = math.max(1, w - self.padding * 2)
|
||||
local _, line_h = GetTextSize(self.font, 'Ay')
|
||||
self._line_h = line_h or 16
|
||||
|
||||
local max_lines = math.max(1, math.floor((h - self.padding * 2) / self._line_h))
|
||||
local lines, trunc = WrapAndEllipsize(self.text, self.font, avail_w_df, max_lines)
|
||||
self._lines = lines
|
||||
self._truncated = trunc
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout(w, h)
|
||||
self:_rebuild_if_needed()
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
self:_rebuild_if_needed()
|
||||
local lines = self._lines or {''}
|
||||
local line_h = self._line_h or 16
|
||||
local total_h = #lines * line_h
|
||||
|
||||
local start_y = self.padding
|
||||
if self.valign == 'center' then
|
||||
start_y = math.floor((h - total_h) / 2)
|
||||
elseif self.valign == 'bottom' then
|
||||
start_y = h - self.padding - total_h
|
||||
end
|
||||
|
||||
surface.SetFont(self.font)
|
||||
for i = 1, #lines do
|
||||
local line = lines[i]
|
||||
local y = start_y + (i - 1) * line_h
|
||||
|
||||
local x = self.padding
|
||||
if self.align == TEXT_ALIGN_CENTER then
|
||||
x = w * 0.5
|
||||
elseif self.align == TEXT_ALIGN_RIGHT then
|
||||
x = w - self.padding
|
||||
end
|
||||
|
||||
draw.SimpleText(line, self.font, x, y, self.color, self.align, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
vgui.Register('MantleText', PANEL, 'EditablePanel')
|
||||
@@ -0,0 +1,35 @@
|
||||
local color_accept = Color(35, 103, 51)
|
||||
|
||||
function Mantle.ui.text_box(title, desc, func)
|
||||
Mantle.ui.menu_text_box = vgui.Create('MantleFrame')
|
||||
Mantle.ui.menu_text_box:SetSize(300, 134)
|
||||
Mantle.ui.menu_text_box:Center()
|
||||
Mantle.ui.menu_text_box:MakePopup()
|
||||
Mantle.ui.menu_text_box:SetTitle(title)
|
||||
Mantle.func.animate_appearance(Mantle.ui.menu_text_box, Mantle.ui.menu_text_box:GetWide(), Mantle.ui.menu_text_box:GetTall(), 0.3, 0.2, nil, 0.9)
|
||||
Mantle.ui.menu_text_box:DockPadding(12, 30, 12, 12)
|
||||
|
||||
local entry = vgui.Create('MantleEntry', Mantle.ui.menu_text_box)
|
||||
entry:Dock(TOP)
|
||||
entry:SetTitle(desc)
|
||||
|
||||
local function apply_func()
|
||||
func(entry:GetValue())
|
||||
|
||||
Mantle.ui.menu_text_box:Remove()
|
||||
end
|
||||
|
||||
entry.OnEnter = function()
|
||||
apply_func()
|
||||
end
|
||||
|
||||
local btn_accept = vgui.Create('MantleBtn', Mantle.ui.menu_text_box)
|
||||
btn_accept:Dock(BOTTOM)
|
||||
btn_accept:SetTall(30)
|
||||
btn_accept:SetTxt(Mantle.lang.get('mantle', 'apply'))
|
||||
btn_accept:SetColorHover(color_accept)
|
||||
btn_accept.DoClick = function()
|
||||
Mantle.func.sound()
|
||||
apply_func()
|
||||
end
|
||||
end
|
||||
82
addons/mantle_darkfated_ultracode/lua/mantle/init.lua
Normal file
82
addons/mantle_darkfated_ultracode/lua/mantle/init.lua
Normal file
@@ -0,0 +1,82 @@
|
||||
--[[
|
||||
* Mantle *
|
||||
GitHub: https://github.com/darkfated/mantle
|
||||
Author's telegram: @darkfated
|
||||
]]--
|
||||
|
||||
local function RunScripts()
|
||||
Mantle.run_cl('config/colors.lua')
|
||||
|
||||
Mantle.run_cl('core/func.lua')
|
||||
Mantle.run_cl('core/vgui.lua')
|
||||
Mantle.run_cl('core/legacy_vgui.lua')
|
||||
Mantle.run_cl('core/menu.lua')
|
||||
|
||||
Mantle.run_cl('core/vgui_elements/button.lua')
|
||||
Mantle.run_cl('core/vgui_elements/checkbox.lua')
|
||||
Mantle.run_cl('core/vgui_elements/color_picker.lua')
|
||||
Mantle.run_cl('core/vgui_elements/derma_menu.lua')
|
||||
Mantle.run_cl('core/vgui_elements/entry.lua')
|
||||
Mantle.run_cl('core/vgui_elements/frame.lua')
|
||||
Mantle.run_cl('core/vgui_elements/player_selector.lua')
|
||||
Mantle.run_cl('core/vgui_elements/radialpanel.lua')
|
||||
Mantle.run_cl('core/vgui_elements/scrollpanel.lua')
|
||||
Mantle.run_cl('core/vgui_elements/slidebox.lua')
|
||||
Mantle.run_cl('core/vgui_elements/tabs.lua')
|
||||
Mantle.run_cl('core/vgui_elements/textbox.lua')
|
||||
Mantle.run_cl('core/vgui_elements/category.lua')
|
||||
Mantle.run_cl('core/vgui_elements/combobox.lua')
|
||||
Mantle.run_cl('core/vgui_elements/table.lua')
|
||||
Mantle.run_cl('core/vgui_elements/text.lua')
|
||||
|
||||
Mantle.run_cl('modules/shadows.lua')
|
||||
Mantle.run_cl('modules/material_url.lua')
|
||||
Mantle.run_sh('modules/notify.lua')
|
||||
Mantle.run_sh('modules/utf8.lua')
|
||||
end
|
||||
|
||||
local function RunAddons()
|
||||
local _, addonsName = file.Find('mantle_addons/*', 'LUA')
|
||||
|
||||
for _, addon in ipairs(addonsName) do
|
||||
if file.Exists('mantle_addons/' .. addon .. '/init.lua', 'LUA') then
|
||||
Mantle.run_sh('mantle_addons/' .. addon .. '/init.lua')
|
||||
end
|
||||
|
||||
if file.Exists('mantle_addons/' .. addon .. '/lang.lua', 'LUA') then
|
||||
local lang = Mantle.run_sh('mantle_addons/' .. addon .. '/lang.lua')
|
||||
Mantle.lang.list[addon] = lang
|
||||
end
|
||||
end
|
||||
|
||||
Mantle.run_sh('core/lang.lua')
|
||||
end
|
||||
|
||||
local function InitLib()
|
||||
if SERVER then
|
||||
resource.AddWorkshop('2924839375') -- DarkFated font
|
||||
resource.AddWorkshop('3126986993') -- Mantle
|
||||
end
|
||||
|
||||
local color_div = Color(168, 109, 236)
|
||||
|
||||
MsgC(color_white, '------------------\n')
|
||||
MsgC(Color(0, 255, 0), '| Mantle LIBRARY |\n')
|
||||
MsgC(color_white, '------------------\n')
|
||||
|
||||
Mantle = Mantle or {
|
||||
lang = { list = {}, default = 'en' },
|
||||
}
|
||||
Mantle.run_cl = SERVER and AddCSLuaFile or include
|
||||
Mantle.run_sv = SERVER and include or function() end
|
||||
Mantle.run_sh = function(f)
|
||||
local client = Mantle.run_cl(f)
|
||||
local server = Mantle.run_sv(f)
|
||||
return SERVER and server or client
|
||||
end
|
||||
|
||||
RunScripts()
|
||||
RunAddons()
|
||||
end
|
||||
|
||||
InitLib()
|
||||
@@ -0,0 +1,40 @@
|
||||
local file, Mat, Fetch, find = file, Material, http.Fetch, string.find
|
||||
local errorMat = Mat('error')
|
||||
local WebImageCache = {}
|
||||
|
||||
--[[
|
||||
Функция для скачивания материала по ссылке и его кэшированного использования
|
||||
]]--
|
||||
function http.DownloadMaterial(url, path, callback, retry_count)
|
||||
if WebImageCache[url] then
|
||||
return callback(WebImageCache[url])
|
||||
end
|
||||
|
||||
local dataPath = 'data/' .. path
|
||||
|
||||
if file.Exists(path, 'DATA') then
|
||||
WebImageCache[url] = Mat(dataPath, 'noclamp mips')
|
||||
|
||||
callback(WebImageCache[url])
|
||||
else
|
||||
Fetch(url, function(img)
|
||||
if !img or find(img, '<!DOCTYPE HTML>', 1, true) then
|
||||
return callback(errorMat)
|
||||
end
|
||||
|
||||
file.Write(path, img)
|
||||
|
||||
WebImageCache[url] = Mat(dataPath, 'noclamp mips')
|
||||
|
||||
callback(WebImageCache[url])
|
||||
end, function()
|
||||
if retry_count and retry_count > 0 then
|
||||
retry_count = retry_count - 1
|
||||
|
||||
http.DownloadMaterial(url, path, callback, retry_count)
|
||||
else
|
||||
callback(errorMat)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
if SERVER then
|
||||
util.AddNetworkString('Mantle-Notify')
|
||||
|
||||
--[[
|
||||
Функция для выведения в чат текста.
|
||||
Можно выводить определённой цели информацию, либо всем, указав вместо pl - true
|
||||
]]--
|
||||
function Mantle.notify(pl, header_color, header, text)
|
||||
net.Start('Mantle-Notify')
|
||||
net.WriteString(header)
|
||||
net.WriteColor(header_color)
|
||||
net.WriteString(text)
|
||||
if pl == true then net.Broadcast() else net.Send(pl) end
|
||||
end
|
||||
else
|
||||
net.Receive('Mantle-Notify', function()
|
||||
local headerText = net.ReadString()
|
||||
local headerColor = net.ReadColor()
|
||||
local headerColorDop = Color(headerColor.r + 10, headerColor.g + 10, headerColor.b + 10)
|
||||
local text = net.ReadString()
|
||||
|
||||
chat.AddText(headerColorDop, '[', headerColor, headerText, headerColorDop, '] ', color_white, text)
|
||||
chat.PlaySound()
|
||||
end)
|
||||
end
|
||||
691
addons/mantle_darkfated_ultracode/lua/mantle/modules/rndx.lua
Normal file
691
addons/mantle_darkfated_ultracode/lua/mantle/modules/rndx.lua
Normal file
File diff suppressed because one or more lines are too long
115
addons/mantle_darkfated_ultracode/lua/mantle/modules/shadows.lua
Normal file
115
addons/mantle_darkfated_ultracode/lua/mantle/modules/shadows.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
local math_sin = math.sin
|
||||
local math_cos = math.cos
|
||||
local math_rad = math.rad
|
||||
local math_ceil = math.ceil
|
||||
|
||||
local function CreateBShadows()
|
||||
BShadows = {}
|
||||
|
||||
local resStr = Mantle.func.sw .. Mantle.func.sh
|
||||
|
||||
BShadows.RenderTarget = GetRenderTarget('BShadows_original_' .. resStr, Mantle.func.sw, Mantle.func.sh)
|
||||
BShadows.RenderTarget2 = GetRenderTarget('BShadows_shadow_' .. resStr, Mantle.func.sw, Mantle.func.sh)
|
||||
BShadows.ShadowMaterial = CreateMaterial('BShadows', 'UnlitGeneric', {
|
||||
['$translucent'] = 1,
|
||||
['$vertexalpha'] = 1,
|
||||
['alpha'] = 1
|
||||
})
|
||||
|
||||
BShadows.ShadowMaterialGrayscale = CreateMaterial('BShadows_grayscale', 'UnlitGeneric', {
|
||||
['$translucent'] = 1,
|
||||
['$vertexalpha'] = 1,
|
||||
['$alpha'] = 1,
|
||||
['$color'] = '0 0 0',
|
||||
['$color2'] = '0 0 0'
|
||||
})
|
||||
|
||||
BShadows.BeginShadow = function()
|
||||
render.PushRenderTarget(BShadows.RenderTarget)
|
||||
|
||||
render.OverrideAlphaWriteEnable(true, true)
|
||||
render.Clear(0, 0, 0, 0)
|
||||
render.OverrideAlphaWriteEnable(false, false)
|
||||
|
||||
cam.Start2D()
|
||||
end
|
||||
|
||||
BShadows.EndShadow = function(intensity, spread, blur, opacity, direction, distance, bool_shadow_only)
|
||||
opacity = opacity or 255
|
||||
direction = direction or 0
|
||||
distance = distance or 0
|
||||
bool_shadow_only = bool_shadow_only or false
|
||||
|
||||
render.CopyRenderTargetToTexture(BShadows.RenderTarget2)
|
||||
|
||||
if blur > 0 then
|
||||
render.OverrideAlphaWriteEnable(true, true)
|
||||
render.BlurRenderTarget(BShadows.RenderTarget2, spread, spread, blur)
|
||||
render.OverrideAlphaWriteEnable(false, false)
|
||||
end
|
||||
|
||||
render.PopRenderTarget()
|
||||
|
||||
BShadows.ShadowMaterial:SetTexture('$basetexture', BShadows.RenderTarget)
|
||||
BShadows.ShadowMaterialGrayscale:SetTexture('$basetexture', BShadows.RenderTarget2)
|
||||
|
||||
local xOffset = math_sin(math_rad(direction)) * distance
|
||||
local yOffset = math_cos(math_rad(direction)) * distance
|
||||
|
||||
BShadows.ShadowMaterialGrayscale:SetFloat('$alpha', opacity / 255)
|
||||
render.SetMaterial(BShadows.ShadowMaterialGrayscale)
|
||||
|
||||
for i = 1, math_ceil(intensity) do
|
||||
render.DrawScreenQuadEx(xOffset, yOffset, Mantle.func.sw, Mantle.func.sh)
|
||||
end
|
||||
|
||||
if !bool_shadow_only then
|
||||
BShadows.ShadowMaterial:SetTexture('$basetexture', BShadows.RenderTarget)
|
||||
render.SetMaterial(BShadows.ShadowMaterial)
|
||||
render.DrawScreenQuad()
|
||||
end
|
||||
|
||||
cam.End2D()
|
||||
end
|
||||
|
||||
BShadows.DrawShadowTexture = function(texture, intensity, spread, blur, opacity, direction, distance, bool_shadow_only)
|
||||
opacity = opacity or 255
|
||||
direction = direction or 0
|
||||
distance = distance or 0
|
||||
bool_shadow_only = bool_shadow_only or false
|
||||
|
||||
render.CopyTexture(texture, BShadows.RenderTarget2)
|
||||
|
||||
if blur > 0 then
|
||||
render.PushRenderTarget(BShadows.RenderTarget2)
|
||||
render.OverrideAlphaWriteEnable(true, true)
|
||||
render.BlurRenderTarget(BShadows.RenderTarget2, spread, spread, blur)
|
||||
render.OverrideAlphaWriteEnable(false, false)
|
||||
render.PopRenderTarget()
|
||||
end
|
||||
|
||||
BShadows.ShadowMaterialGrayscale:SetTexture('$basetexture', BShadows.RenderTarget2)
|
||||
|
||||
local xOffset = math_sin(math_rad(direction)) * distance
|
||||
local yOffset = math_cos(math_rad(direction)) * distance
|
||||
|
||||
BShadows.ShadowMaterialGrayscale:SetFloat('$alpha', opacity / 255)
|
||||
render.SetMaterial(BShadows.ShadowMaterialGrayscale)
|
||||
|
||||
for i = 1, math_ceil(intensity) do
|
||||
render.DrawScreenQuadEx(xOffset, yOffset, Mantle.func.sw, Mantle.func.sh)
|
||||
end
|
||||
|
||||
if !bool_shadow_only then
|
||||
BShadows.ShadowMaterial:SetTexture('$basetexture', texture)
|
||||
render.SetMaterial(BShadows.ShadowMaterial)
|
||||
render.DrawScreenQuad()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
CreateBShadows()
|
||||
|
||||
hook.Add('OnScreenSizeChanged', 'Mantle.Shadows', function()
|
||||
CreateBShadows()
|
||||
end)
|
||||
@@ -0,0 +1,23 @@
|
||||
if utf8 == nil then
|
||||
utf8 = {
|
||||
charpattern = '[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*'
|
||||
}
|
||||
end
|
||||
|
||||
local uc_lc = {['А']='а',['Б']='б',['В']='в',['Г']='г',['Д']='д',['Е']='е',['Ё']='ё',['Ж']='ж',['З']='з',['И']='и',['Й']='й',['К']='к',['Л']='л',['М']='м',['Н']='н',['О']='о',['П']='п',['Р']='р',['С']='с',['Т']='т',['У']='у',['Ф']='ф',['Х']='х',['Ц']='ц',['Ч']='ч',['Ш']='ш',['Щ']='щ',['Ъ']='ъ',['Ы']='ы',['Ь']='ь',['Э']='э',['Ю']='ю',['Я']='я'}
|
||||
local lc_uc = {}
|
||||
|
||||
for uc, lc in pairs(uc_lc) do
|
||||
lc_uc[lc] = uc
|
||||
end
|
||||
|
||||
setmetatable(uc_lc, {__index = function(_, char) return char:lower() end})
|
||||
setmetatable(lc_uc, {__index = function(_, char) return char:upper() end})
|
||||
|
||||
function utf8.lower(text)
|
||||
return text:gsub(utf8.charpattern, uc_lc)
|
||||
end
|
||||
|
||||
function utf8.upper(text)
|
||||
return text:gsub(utf8.charpattern, lc_uc)
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
local tabl = {}
|
||||
|
||||
tabl['en'] = {
|
||||
apply = 'Apply',
|
||||
|
||||
table_wrong_args = 'MantleTable Error: Invalid number of arguments',
|
||||
table_copy = 'Copy',
|
||||
table_delete_row = 'Delete row',
|
||||
|
||||
player_title = 'Player Selector',
|
||||
player_offline = 'Disconnected',
|
||||
player_close = 'Close',
|
||||
player_ping = 'ms',
|
||||
|
||||
frame_title = 'Title',
|
||||
frame_alpha = 'Transparency',
|
||||
frame_move_from_menu = 'Move from menu',
|
||||
frame_close_window = 'Close window',
|
||||
|
||||
color_title = 'Color Picker',
|
||||
color_cancel = 'Cancel',
|
||||
color_select = 'Select',
|
||||
|
||||
btn_default = 'Button',
|
||||
|
||||
entry_default_placeholder = 'Enter text'
|
||||
}
|
||||
|
||||
tabl['ru'] = {
|
||||
apply = 'Применить',
|
||||
|
||||
table_wrong_args = 'MantleTable Error: Неверное количество аргументов',
|
||||
table_copy = 'Копировать',
|
||||
table_delete_row = 'Удалить строку',
|
||||
|
||||
player_title = 'Выбор игрока',
|
||||
player_offline = 'Вышел',
|
||||
player_close = 'Закрыть',
|
||||
player_ping = 'мс',
|
||||
|
||||
frame_title = 'Заголовок',
|
||||
frame_alpha = 'Прозрачность',
|
||||
frame_move_from_menu = 'Передвижение из меню',
|
||||
frame_close_window = 'Закрыть окно',
|
||||
|
||||
color_title = 'Выбор цвета',
|
||||
color_cancel = 'Отмена',
|
||||
color_select = 'Выбрать',
|
||||
|
||||
btn_default = 'Кнопка',
|
||||
|
||||
entry_default_placeholder = 'Введите текст'
|
||||
}
|
||||
|
||||
return tabl
|
||||
BIN
addons/mantle_darkfated_ultracode/materials/mantle/close_btn.png
Normal file
BIN
addons/mantle_darkfated_ultracode/materials/mantle/close_btn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 297 B |
Binary file not shown.
|
After Width: | Height: | Size: 204 B |
BIN
addons/mantle_darkfated_ultracode/materials/mantle/slider.png
Normal file
BIN
addons/mantle_darkfated_ultracode/materials/mantle/slider.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 474 B |
BIN
addons/mantle_darkfated_ultracode/sound/mantle/btn_click.ogg
Normal file
BIN
addons/mantle_darkfated_ultracode/sound/mantle/btn_click.ogg
Normal file
Binary file not shown.
BIN
addons/mantle_darkfated_ultracode/sound/mantle/ratio_btn.ogg
Normal file
BIN
addons/mantle_darkfated_ultracode/sound/mantle/ratio_btn.ogg
Normal file
Binary file not shown.
Reference in New Issue
Block a user