Relicensing the code and removing it from github. Last commit was April 2021

This commit is contained in:
Tracy Rust 2023-12-10 22:37:17 -05:00
commit ef5699db9e
309 changed files with 19481 additions and 0 deletions

219
CMakeLists.txt Normal file
View File

@ -0,0 +1,219 @@
#This file is part of the IVD project and is licensed under LGPL-3.0-only
cmake_minimum_required(VERSION 3.12)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
project(IVD CXX)
set(CMAKE_CXX_STANDARD 17)
list(APPEND ivd_core_sources
src/runtimeattribute.cpp
src/runtimeattribute.h
src/attributepositionpair.h
src/runtimeattributeset.cpp
src/runtimeattributeset.h
src/canvas.h
src/color.cpp
src/color.h
src/compiler.cpp
src/compiler.h
src/defaults.cpp
src/defaults.h
src/displayitem.cpp
src/displayitem.h
src/driver.h
src/element.h
src/environment.cpp
src/environment.h
src/expression.cpp
src/expression.h
src/geometry.h
src/geometryproposal.h
src/keywords.h
src/standardstatekeys.h
src/statekey.h
src/statemanager.h
src/statemanager.cpp
src/states.h
src/text.cpp
src/text.h
src/valuekey.cpp
src/valuekey.h
src/corefonts.h
src/graph.cpp
src/graph.h
src/codeposition.h
src/virtualstatekey.h
src/virtualstatekey.cpp
src/binaryexpressionprinter.h
src/cbindings.cpp
src/referenceattribute.h
src/referenceattribute.cpp
src/referenceattributeset.h
src/referenceattributeset.cpp
src/attributebodytypes.h
src/user_include/IVD_c.h
src/user_include/IVD_status.h
src/user_include/IVD_constants_c.h
src/user_include/cpp/IVD_cpp.h
src/user_include/cpp/IVD_geometry.h
src/user_include/cpp/IVD_geometry_proposal.h
src/widget.h
src/shaping/line.h
src/widgets/boxlayout.h
src/widgets/boxlayout.cpp
src/widgets/stacklayout.h
src/widgets/stacklayout.cpp
src/widgets/image.h
src/widgets/image.cpp
src/specific_driver_sdl/cairocanvas.cpp
src/specific_driver_sdl/cairocanvas.h
src/specific_driver_sdl/sdldriver.cpp
src/specific_driver_sdl/sdldriver.h
src/specific_driver_sdl/sdlwindow.cpp
src/specific_driver_sdl/sdlwindow.h
src/specific_driver_sdl/textdriver.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontsansbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontsansbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontsansboldbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontsansitalicbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontsansbolditalicbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontserifbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontserifboldbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontserifitalicbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontserifbolditalicbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontmonobinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontmonoboldbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontmonoitalicbinary.cpp
${CMAKE_CURRENT_BINARY_DIR}/corefontmonobolditalicbinary.cpp
contrib/rustutils/lexcompare.h
contrib/rustutils/routine.h
contrib/rustutils/easyuniquepointer.h
)
find_package(OpenImageIO REQUIRED)
find_package(reprodyne 1.0.0 REQUIRED)
find_package(Freetype REQUIRED)
find_package(Catch2 REQUIRED)
find_package(SDL2 REQUIRED)
SET(IVD_COMMON_LIBS
SDL2
cairo
harfbuzz
freetype
OpenImageIO)
SET(IVD_INTERNAL_INCLUDES
src
contrib
${REPRODYNE_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
${OPENIMAGEIO_INCLUDES})
add_library(ivd SHARED ${ivd_core_sources})
target_include_directories(ivd PRIVATE PRIVATE ${IVD_INTERNAL_INCLUDES})
target_link_libraries(ivd PRIVATE ${IVD_COMMON_LIBS})
set_target_properties(ivd PROPERTIES PUBLIC_HEADER
"${PROJECT_SOURCE_DIR}/src/user_include/cpp/IVD_geometry_proposal.h;${PROJECT_SOURCE_DIR}/src/user_include/IVD_c.h;${PROJECT_SOURCE_DIR}/src/user_include/cpp/IVD_geometry.h;${PROJECT_SOURCE_DIR}/src/user_include/IVD_constants.h;${PROJECT_SOURCE_DIR}/src/user_include/cpp/IVD_cpp.h;${PROJECT_SOURCE_DIR}/src/user_include/IVD_status.h")
add_executable(ivdRepro ${ivd_core_sources} src/tests/recordplayback.cpp)
target_include_directories(ivdRepro PRIVATE ${IVD_INTERNAL_INCLUDES})
target_link_libraries(ivdRepro PRIVATE ${IVD_COMMON_LIBS} reprodyne)
target_compile_definitions(ivdRepro PUBLIC REPRODYNE_AVAILABLE)
find_package(Python3 REQUIRED)
function(addFont sourcettf constantsymbol)
set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${constantsymbol}binary.cpp)
add_custom_command(DEPENDS ${sourcettf} binarysourcegenerator.py
COMMAND ${Python3_EXECUTABLE} binarysourcegenerator.py
${sourcettf} corefonts.h ${constantsymbol} ${outfile}
OUTPUT ${outfile}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endfunction()
addFont("contrib/fonts/LiberationSans-Regular.ttf" "corefontsans")
addFont("contrib/fonts/LiberationSans-Bold.ttf" "corefontsansbold")
addFont("contrib/fonts/LiberationSans-Italic.ttf" "corefontsansitalic")
addFont("contrib/fonts/LiberationSans-BoldItalic.ttf" "corefontsansbolditalic")
addFont("contrib/fonts/LiberationSerif-Regular.ttf" "corefontserif")
addFont("contrib/fonts/LiberationSerif-Bold.ttf" "corefontserifbold")
addFont("contrib/fonts/LiberationSerif-Italic.ttf" "corefontserifitalic")
addFont("contrib/fonts/LiberationSerif-BoldItalic.ttf" "corefontserifbolditalic")
addFont("contrib/fonts/LiberationMono-Regular.ttf" "corefontmono")
addFont("contrib/fonts/LiberationMono-Bold.ttf" "corefontmonobold")
addFont("contrib/fonts/LiberationMono-Italic.ttf" "corefontmonoitalic")
addFont("contrib/fonts/LiberationMono-BoldItalic.ttf" "corefontmonobolditalic")
configure_file("contrib/test-images/jpeg_test_article.jpg" "jpeg_test_article.jpg" COPYONLY)
configure_file("contrib/test-images/png_test_article.png" "png_test_article.png" COPYONLY)
add_executable(ivdruntime src/tests/lightruntime.cpp)
target_include_directories(ivdruntime PRIVATE src)
target_link_libraries(ivdruntime PRIVATE ivd)
add_executable(ivdserializingcompiler src/tests/serializingcompiler.cpp)
target_include_directories(ivdserializingcompiler PRIVATE src contrib)
target_link_libraries(ivdserializingcompiler PRIVATE ivd)
include(TestBigEndian)
TEST_BIG_ENDIAN(systemIsBigEndian)
if(systemIsBigEndian)
add_compile_definitions(BIG_ENDIAN_SYSTEM)
else()
add_compile_definitions(LITTLE_ENDIAN_SYSTEM)
endif()
set(IVD_MAJOR_VERSION 0)
set(IVD_MINOR_VERSION 1)
set(IVD_PATCH_VERSION 0)
set(IVD_API_VERSION ${IVD_MAJOR_VERSION}.${IVD_MINOR_VERSION})
set(IVD_VERSION ${IVD_API_VERSION}.${IVD_PATCH_VERSION})
set(IVD_DEST_NAME IVD-${IVD_API_VERSION})
set(IVD_USER_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include/${IVD_DEST_NAME})
install(TARGETS ivd EXPORT ivd-targets
ARCHIVE
DESTINATION lib/${IVD_DEST_NAME}
PUBLIC_HEADER
DESTINATION ${IVD_USER_INCLUDE_DIRS})
include(CMakePackageConfigHelpers)
write_basic_package_version_file(ivd-config-version.cmake
VERSION ${IVD_VERSION}
COMPATIBILITY SameMajorVersion)
configure_file(ivd-config.cmake.in
${CMAKE_BINARY_DIR}/ivd-config.cmake @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/ivd-config.cmake ${CMAKE_BINARY_DIR}/ivd-config-version.cmake
DESTINATION lib/cmake/${IVD_DEST_NAME})
install(EXPORT ivd-targets
DESTINATION lib/cmake/${IVD_DEST_NAME})

674
LICENSE.txt Normal file
View 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>.

500
README.md Normal file
View File

@ -0,0 +1,500 @@
# IVD - Interactive Visual Design Language
IVD is a declarative GUI programming language and framework implementation. The goal is to make it so that you can describe *how* a interface should work and leave the rest to the computer, facilitating highly dynamic GUI programming, whilst maintaining maintainability.
# Why?
The decision to create IVD was not reached lightly. I knew it would be a huge undertaking (Although I underestimated how huge...), and spent considerable time trying to talk myself out of doing it.
Aside from looking at all the GUI toolkits I could find before I broke ground, I had experienced developing user interfaces in the classical way (Qt/GTK), the "modern" way (HTML/CSS/JavaScript), and played around a bit with QML (which inspired some aspects of this project). All of these had their pros and cons, but none of them seemed to represent a true advancement in the problem space, and I'm arrogant enough that I thought I could maybe move the needle myself.
# A Quick(ish) and Mostly Incomplete Rundown of IVD
## IVD is Not Ready
IVD is still in heavy development and as such this readme should be treated as a writeup of the project, rather than as a guide on using it. It's okay if you don't get the syntax completely, it's not written so that you are supposed to; just read on and the high level ought to stand out at least.
## IVD is *Not* CSS++
Before we get started, it's important to acknowledge that although the syntax is superficially similar, the theory of IVD is *very* different from the theory of CSS. Elements in IVD aren't bound to models by default, and they don't inherit attributes from their "parents" (Because they don't have static parents). About the only concepts that carry over from CSS are some attribute names (Although I've renamed some that are otherwise equivalent, just to piss you off), and the box model for styling. Most everything else is either taken more from traditional GUI toolkits or is novel.
IVD allows for visual elements to be free and not bound to the model, so that your data model isn't corrupted with irrelevant noise which only exists for the presentation, as is always the case with `<DIV>` tags in any reasonably complex webpage. As such, it allows your data to be truly semantic.
CSS is for *styling* models, and requires something like HTML and JavaScript to create a GUI with it. IVD on the other hand is for *defining* complete user interfaces with [*style*](https://i.redd.it/vq2q5dqh16qy.png).
## Events Kinda Suck, State System to The Rescue
One great problem I think is the low level nature of typical GUI events. Take for example the case of an element needing to style itself differently when hovered in a scroll area. A naive approach involves monitoring "mouse motion in" and "mouse motion out" events, and setting your internal hover state accordingly. This is all fine and good until you have the cursor hovering over an element, and without moving the mouse, the user scrolls, suddenly throwing the hover out of sync.
It's easy enough to fix said issue, but even easier to break again because the solution isn't intuitive. It's fun to watch applications gain and lose this bug as they go through updates. The idea is to leave these tricky edge cases to the GUI framework, instead of fixing and breaking (solved) problems like this all the time while in the middle of trying to fix something completely unrelated.
The problem with individual widgets processing events is that they have no context (At least at the level that your typical GUI events exist, it is certainly possible to have higher level events, but IVD as a language has other benefits as well). In IVD, the environment is responsible for determining whether an item is hovered or not (Which was in part inspired by CSS's pseudo-classes):
#element-name
{
color: red;
state this.IVD-Item-Hovered:
color: blue;
state ::.IVD-Mouse-Clicked:
color: green;
state ::.IVD-Mouse-Clicked & this.IVD-Item-Hovered:
color: purple;
}
Unlike CSS's pseudo-classes, IVD has a very powerful state system and allows boolean comparisons on states:
state this.aState & (anotherState | !stateeee):
"Overlapping" attributes, where two active states define a different value for a given attribute, override in descending order:
# //anonymous element because you shouldn't be forced to name things you don't want to
{
attr: one;
state sky-is-blue:
attr: two;
state grass-is-green:
attr: three; //"three" is chosen by the runtime
}
Elements can manipulate states, even in other elements:
#an-element
{
state coordinatedState:
color: blue;
}
#
{
state aState:
induce-state: an-element.coordinatedState;
//Can also toggle, unset, etc
toggle-state: state;
unset-state: state;
}
Event-like behavior is handled by "trigger-states". These are states that are guaranteed by the runtime to last only a single frame. This allows you to set processes external to IVD in motion using the `trigger` attribute:
#
{
state this.clicked:
trigger: IVD-Core-Quit;
}
Say you have a group of states, and only one can be active at any given time. It could be tabs, or a radio-box selection, etc. IVD can manage the exclusivity for you:
#
{
radio-state: one, two, three;
state one:
state two:
state three:
}
And then only the last state to be set will be active in that element at any given time.
## Positioning
Widget hierarchies tend to be very ridged. In traditional GUI toolkits, this is because each widget "owns" it's children, and reparenting is an arduous task. HTML isn't much better, where even if you use JavaScript to restructure the DOM, it still has a specific default defined in a very different way from how your dynamic structure is defined.
This makes it difficult to create GUIs that are easy to re-arrange.
The two points that make IVD different here are that elements are completely symmetric, and the fact that the visual element's structure isn't tightly bound to a model. When in use, a model's hierarchy only applies locally, and is largely optional (The element hierarchy doesn't necessarily have to map 1:1 to the model). [More on models below](#models).
IVD's structure being symmetric just means that positioning is always conditional. An element must choose to position itself within another element. It always starts out flat:
#
{
state some-condition:
position-within: window;
state some-other-condition:
position-within: another-element;
}
## Layouts/Materials
One place where traditional toolkits beat HTML/CSS I think unequivically, is in their layout system. Floats... Are unnecessarily complicated to reason about, and enough tears have been shed over that system that I feel I need not ever speak of it again.
IVD follows a typical nested layout system. Built in layouts include hbox and vbox, for horizontal and vertical rows respectively, and the stack layout for layering.
Given the following example:
#window
{
position-within: Environment; //Give us a window
layout: hbox;
}
#
{
position-within: window;
text: "Aye";
}
#
{
position-within: window;
text: "Bye";
}
#
{
position-within: window;
text: "Cye";
}
The following is produced (please excuse the crudity of this model, I didn't have time to build it to scale or paint it):
[AyeByeCye]
That's great, but then the order is almost arbitrary (It's actually based on the order of element declaration but bear with me). To reserve specific "slots", we have a feature called "named-cells":
#window
{
position-within: Environment; //Give us a window
layout: hbox;
named-cells: first, middle, last;
}
#
{
position-within: window.last;
text: "Cye";
}
#
{
position-within: window.first;
text: "Aye";
}
#
{
position-within: window.middle;
text: "Bye";
}
Which produces the same output:
[AyeByeCye]
This makes it painless to slip a column in between two elements later in the development cycle, without touching anything that already works. Empty cells are simply ignored, so they're great to declare where a notification or context popout should appear when it feels like it.
Of course, it also makes it trivial to rearrange items:
#window
{
named-cells: first, middle, last;
state a-state:
named-cells: middle, first, last; //Please use better names tho
}
Which would produce:
[ByeAyeCye]
The built-in layouts should cover 95% (totally legit stat) of use cases simply enough. But for special cases you can extend IVD with custom materials.
## Models
A model may define a hierarchy, but elements bound to specific model items don't necessarily have to reflect it directly, and are free to position items in a root element or separate window, for example.
The IVD philosophy is that a model shouldn't *ridgedly* define the presentation. Contrast with HTML where absolutely everything is a part of the DOM in a very specific order and hierarchy, even unrelated models must be encoded in an arbitrary order.
There are two types of elements in IVD. "Free elements", which are not bound to a model and there is exactly one instance, and "enumerated elements", which are bound to a model instance, and there can be zero or more of them (one for each instance).
In the previous examples, you've seen elements declared as such:
#name-optional {}
This instantiates a single element.
Suppose you have a model uncreatively named `my-model`:
#an-enumerated-element -> my-model {}
This creates a single instance of `an-enumerated-element` for every item in `my-model`. A "model" simply declares a list of model items. The process of binding elements to model items is known in IVD parlance as "enumeration", as elements are *enumerated* by the model.
A model item can define strings, integers, trigger slots and states. All of which may be used by elements in IVD:
# -> model-name
{
text: model.the-text;
state model.a-state:
width: model.width-for-a-state;
state this.clicked:
trigger: model.react-to-click;
}
References to the model within an element are prefixed with the keyword `model` and not the model's identifier because it allows you to easily rename the model, use generic models in classes, and because I felt like it.
Values from a model item that are used by IVD are always kept in sync. If the value changes in the model, the change is reflected in the IVD runtime.
Model states can be manipulated directly by IVD code as well:
# -> arbitrarily-named-model-42
{
state x:
induce-state: model.a-model-state;
}
Models themselves can contain child items, allowing for complex nested data structures.
You can't position a free element within an enumerated element, you can however, position an enumerated element within a free element, or position an enumerated element within another enumerated element which share a model in common:
#Nietzsche -> model-name;
# -> model-name
{
position-within: Nietzsche;
}
The runtime will find the correct instance of `Nietzsche` to position the anonymous element within.
This actually works with any common ancestor, suppose the following:
#an-elephant -> elephants
{
layout: vbox;
}
#leg -> elephants::legs
{
position-within: an-elephant;
}
This enumerates an element for each instance of `elephant` and each instance of `elephant::legs`. Notice that before the `position-within` attribute is set, the item `leg` has no visual relationship to `an-elephant`. Common model deduction is what allows you to position elements enumerated across a model, within elements enumerated by *that model's ancestor*.
Suppose you want ordered items (As one often does). Perhaps the model has triggers defined for sorting the model according to different criteria. The order of elements in IVD can be bound to the model order:
#an-elephant -> elephants
{
layout: vbox;
model-order: enable; //Sort child elements according to model
}
#leg -> elephants::legs
{
position-within: an-elephant;
}
The plan is to be able to rearrange items in IVD, and then have the new order backpropogated to the model as well.
## Equation Solver
IVD allows you to define scalar constraints as equations which are kept up-to-date automatically:
#element1
{
width: other-element.height * 2;
}
The trouble with the above, is that it isn't flexible. What if you just really wanted for `element1.width == 200` to be true, but without violating the constraint?
#element2
{
state I-want-to-set-something:
set: element1 = 200; //error because it doesn't know what to do
}
Wouldn't it be nice if you could get IVD to figure out what `other-element.height` needs to be in order for `element1.width == 200` to be true?
#element1
{
width: [other-element.height] * 2; //Declare which value in the expression is weak
}
#element2
{
state I-want-to-set-something:
set: element1 = 200; //Works, because now it knows to solve for other-element.height
}
What happens in the above is that the expression in `element1.width` is solved for `other-element.height`, and the result is backpropogated to `other-element.height` (Which may be defined as a variable or an expression with a weak value, it can be turtles all the way down). Once that is updated, the expression in `element1.width` is reevaluated.
It's important to think of this as more of a suggestion than an absolute order. `other-element.height` might have a min/max constraint which rounds off the value being propogated, and then ***that*** value is what is observed when `element1.width` is reevaluated. Nothing is ever left in an inconsistent state.
Scalars declared by a model can be back-propogated to as well:
#element1 -> my-model
{
width: [model.a-val] * 2;
}
Everything always obeys the constraints as defined, but you also have the power to update the observed values arbitrarily, giving you the best of both worlds.
## Classes
Classes are very simple. They're really just templates from which attributes are copied:
.class-name
{
color: blue;
}
# : class-name
{
//color is blue
}
Shorthand version if you don't need to declare anything in the body of the deriving element:
# : class-name;
An element can derive from an arbitrary number of classes, and the attributes are overriden in the order that the classes are declared:
.another-class
{
color: yellow;
state ::.IVD-Mouse-Motion:
color: green;
}
# : another-class, class-name
{
//color is blue
state ::.IVD-Mouse-Motion:
//color is green
}
# : class-name, another-class //Class list order different
{
//color is yellow this time
state ::.IVD-Mouse-Motion:
//color is still green because it's not in conflict
}
## Remorial Classes for Code Reuse
And last but *certainly* not least.
Suppose you have the following construct:
#contextual-dialog
{
layout: vbox;
named-cells: message-cell, input-cell, confirmation-cell;
}
#message-area
{
position-within: contextual-dialog.message-cell;
layout: hbox;
named-cells: image-cell, text-cell;
}
#message-image
{
position-within: message-area.image-cell;
image: "image-uri";
}
#message-text
{
position-within: message-area.text-cell;
text: "Enter info below";
}
//etc just use your imagination for the confirmation cell
Everything is fine and good until... You need to reuse that. You can't simply use a class because a class only helps with a single element, and the above can only work with several elements.
Remorial classes are a way of defining a "composite" element. You define the class as normal, and then attach "remoras" (get it?), which are just a special kind of element to it. Whenever an element derives from this class, a copy of each remora is also spun up as well, facilitating reuse of complex elements.
And the syntax is quite simple:
.contextual-dialog
{
layout: vbox;
named-cells: message-cell, input-cell, confirmation-cell;
}
@contextual-dialog.message-area
{
position-within: @.message-cell;
layout: hbox;
named-cells: image-cell, text-cell;
}
@conceptual-dialog.message-image
{
position-within: @::message-area.image-cell;
image: "image-uri";
}
@conceptual-dialog.message-text
{
position-within: @::message-area.text-cell;
text: "Enter info below";
}
//etc
# : contextual-dialog;
This is equivalent to the previous example, except that it is reusable, of course.
Where the remora operator (`@`) is used within an element, it is substituted by the actual instance name given to the element which derives from the remorial class (Which is auto-generated in the above example as it's an anonymous element).
One special feature of remora substitution is that you can address any remora in the "school" (get it???) from any other element in the school using the `@::element-name` syntax, which is substituted with the name generated for it by the compiler. This allows for a remorial class to export values from a child remora (see /src/tests/valid/remoravaluekeysubstitution.ivd) which are all otherwise unaddressable, but the implications of this are outside the scope of this little hoe-down.
The remora example above is obviously incomplete. The biggest failing is that it really should be bound to a model in order to have a place to actually send input data and triggers for buttons.
Remoras work with nested models, and common parent deduction and all that good stuff as well. They're just an additional type of template which tag alongside otherwise normal classes.
Again, remoras are just syntactic sugar, they are expanded by the compiler. The resulting elements are exactly the same as if they had been defined manually. A little bit of witchcraft and maybe some symbol substitution makes it all come together quite nicely~
## But That's Not All!
This is by no means a complete overview of the features developed or in development for IVD. We haven't even mentioned the (working!) animation system or the ability to declare expressions (which is to allow for complex widget interactions such as scrollbars or sliders affecting viewports, all defined within IVD), or the C (see /src/user_include/IVD_c.h) and C++ (see /src/user_include/IVD_cpp.h) bindings... It is simply meant to give you a taste for the project.
# What's Working?
In no particular order:
- The compiler, which produces friendly error messages.
- Remorial class instantiation.
- Basic element styling.
- States and state expressions.
- The layout/material system.
- Text layouts.
- Models, enumeration, common ancestor deduction, etc.
- Animations of arbitrary scalar attributes.
- The equation solver (Not tested thoroughly enough for my tastes though).
- And a bunch of other stuff, probably.
# Contributing
\*sounds of deranged cackling echo in the distance\*
please help
# Credits
Created and developed by Tracy Rust (tracy@enesda.com).
# License
IVD is licensed under the terms of the LGPL-3.0-only

37
binarysourcegenerator.py Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
#This file is part of the IVD project and is licensed under LGPL-3.0-only
from sys import argv
blah, inputBinaryFile, includePath, variableName, outputName = argv
output = []
with open(inputBinaryFile, "rb") as f:
byte = f.read(1)
while byte:
output.append("0x" + byte.hex())
byte = f.read(1)
with open(outputName, "w") as f:
f.write("//This file was generated at build time.\n")
f.write("#include \"%s\"\n" % includePath)
f.write("const unsigned char %s[] = {\n" % variableName)
length = len(output)
i = 1;
for char in output:
f.write(char)
if i != length:
f.write(",")
if i and not i % 12:
f.write("\n")
elif i != length:
f.write(" ")
i += 1
f.write("};\n")
f.write("const int %sSize = %d;\n" % (variableName, len(output)))

102
contrib/fonts/LICENSE Normal file
View File

@ -0,0 +1,102 @@
Digitized data copyright (c) 2010 Google Corporation
with Reserved Font Arimo, Tinos and Cousine.
Copyright (c) 2012 Red Hat, Inc.
with Reserved Font Name Liberation.
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
PREAMBLE The goals of the Open Font License (OFL) are to stimulate
worldwide development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to provide
a free and open framework in which fonts may be shared and improved in
partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves.
The fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such.
This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components
as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting ? in part or in whole ?
any of the components of the Original Version, by changing formats or
by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer
or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this license, and must not be distributed
under any other license. The requirement for fonts to remain under
this license does not apply to any document created using the Font
Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,42 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include <memory>
namespace RustUtils
{
//I know it's frowned upon to inherit from std classes but fucking byte me.
//This is just for the trivial case where I want a deep copy and nothing fancy.
template<typename T>
class DeepCopyableUnique : public std::unique_ptr<T>
{
public:
typedef std::unique_ptr<T> Parent;
DeepCopyableUnique() noexcept {}
DeepCopyableUnique(const DeepCopyableUnique& other) noexcept:
Parent(other.get() ? std::make_unique<T>(*other.get())
: nullptr)
{}
DeepCopyableUnique(const Parent&& other) noexcept:
Parent(other)
{}
DeepCopyableUnique& operator=(const DeepCopyableUnique& other) noexcept
{
if(other.get())
Parent::operator=(std::make_unique<T>(*other.get()));
else Parent::reset();
return *this;
}
DeepCopyableUnique& makeCopy(const T& other)
{
Parent::operator=(std::make_unique<T>(other));
return *this;
}
};
}//RustUtils

View File

@ -0,0 +1,353 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef RUSTUTILS_LEXCOMPARE
#define RUSTUTILS_LEXCOMPARE
#include <tuple>
//Once upon a time I thought 7 outta be enough. But it proved insufficient, so then I made it 17.
//... Then that wasn't enough... So now it's 22...
#define RUSTUTILS_MACRO_OVERLOAD(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, MACRO, ...) MACRO
#define RUSTUTILS_UNPACK_FINAL(object, field) object . field
#define RUSTUTILS_UNPACK22(object, field22, field21, field20, field19, field18, field17, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field22) , \
RUSTUTILS_UNPACK_FINAL(object, field21) , \
RUSTUTILS_UNPACK_FINAL(object, field20) , \
RUSTUTILS_UNPACK_FINAL(object, field19) , \
RUSTUTILS_UNPACK_FINAL(object, field18) , \
RUSTUTILS_UNPACK_FINAL(object, field17) , \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK21(object, field21, field20, field19, field18, field17, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field21) , \
RUSTUTILS_UNPACK_FINAL(object, field20) , \
RUSTUTILS_UNPACK_FINAL(object, field19) , \
RUSTUTILS_UNPACK_FINAL(object, field18) , \
RUSTUTILS_UNPACK_FINAL(object, field17) , \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK20(object, field20, field19, field18, field17, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field20) , \
RUSTUTILS_UNPACK_FINAL(object, field19) , \
RUSTUTILS_UNPACK_FINAL(object, field18) , \
RUSTUTILS_UNPACK_FINAL(object, field17) , \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK19(object, field19, field18, field17, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field19) , \
RUSTUTILS_UNPACK_FINAL(object, field18) , \
RUSTUTILS_UNPACK_FINAL(object, field17) , \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK18(object, field18, field17, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field18) , \
RUSTUTILS_UNPACK_FINAL(object, field17) , \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK17(object, field17, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field17) , \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK16(object, field16, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field16) , \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK15(object, field15, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field15) , \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK14(object, field14, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field14) , \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK13(object, field13, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field13) , \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK12(object, field12, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field12) , \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK11(object, field11, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field11) , \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK10(object, field10, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field10) , \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK9(object, field9, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field9) , \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK8(object, field8, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field8) , \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK7(object, field7, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field7) , \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK6(object, field6, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field6) , \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK5(object, field5, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field5) , \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK4(object, field4, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field4) , \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK3(object, field3, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field3) , \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK2(object, field2, field1) \
RUSTUTILS_UNPACK_FINAL(object, field2) , \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK1(object, field1) \
RUSTUTILS_UNPACK_FINAL(object, field1)
#define RUSTUTILS_UNPACK(object, fields...) \
RUSTUTILS_MACRO_OVERLOAD(fields, \
RUSTUTILS_UNPACK22, \
RUSTUTILS_UNPACK21, \
RUSTUTILS_UNPACK20, \
RUSTUTILS_UNPACK19, \
RUSTUTILS_UNPACK18, \
RUSTUTILS_UNPACK17, \
RUSTUTILS_UNPACK16, \
RUSTUTILS_UNPACK15, \
RUSTUTILS_UNPACK14, \
RUSTUTILS_UNPACK13, \
RUSTUTILS_UNPACK12, \
RUSTUTILS_UNPACK11, \
RUSTUTILS_UNPACK10, \
RUSTUTILS_UNPACK9, \
RUSTUTILS_UNPACK8, \
RUSTUTILS_UNPACK7, \
RUSTUTILS_UNPACK6, \
RUSTUTILS_UNPACK5, \
RUSTUTILS_UNPACK4, \
RUSTUTILS_UNPACK3, \
RUSTUTILS_UNPACK2, \
RUSTUTILS_UNPACK1)(object, fields)
#define RUSTUTILS_DEFINE_COMP(type, fields...) \
friend bool operator<(const type & left, const type & right) \
{ \
return std::tie(RUSTUTILS_UNPACK(left, fields)) < \
std::tie(RUSTUTILS_UNPACK(right, fields)); \
} \
friend bool operator!=(const type & left, const type & right) \
{ \
return left < right || right < left; \
} \
friend bool operator==(const type & left, const type & right) \
{ \
return !(left != right); \
}
#endif // RUSTUTILS_LEXCOMPARE

20
contrib/rustutils/routine.h Executable file
View File

@ -0,0 +1,20 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef ROUTINE_H
#define ROUTINE_H
namespace RustUtils
{
namespace Routine
{
template<typename T>
void appendContainer(T& left, const T& right)
{
left.insert(left.end(), right.cbegin(), right.cend());
}
}//Routine
}//RustUtils
#endif // ROUTINE_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

3
ivd-config.cmake.in Normal file
View File

@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/ivd-targets.cmake)
set(IVD_INCLUDE_DIRS "@IVD_USER_INCLUDE_DIRS@")

34
ivd.vim Executable file
View File

@ -0,0 +1,34 @@
"This file is part of the IVD project and is licensed under LGPL-3.0-only
spec bleeding;
" Custom syntax for the IVD language.
" IVD: Interactive Visual Design
if exists("b:current_syntax")
finish
endif
echom "IVD: Interactive Visual Design"
syntax keyword ivdLabel state nextgroup=ivdCustomState skipwhite
syntax keyword ivdKeyword exclusive
syntax keyword ivdAttributes position position-within margin layout
syntax keyword ivdAttributes width height greyout
syntax match ivdCustomState "\i\+" contained
highlight link ivdCustomState Identifier
syntax match ivdComment "\v//.*$"
highlight link ivdComment Comment
highlight link ivdLabel Label
highlight link ivdKeyword Keyword
highlight link ivdAttributes Type
let b:current_syntax = "ivd"

28
src/attributebodytypes.h Normal file
View File

@ -0,0 +1,28 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include <map>
namespace IVD
{
typedef unsigned int KeyType;
struct AttributeBodyTypes
{
bool expression;
bool property;
bool stringLiteral;
bool userToken;
bool userTokenList;
bool stateKeyList;
bool singleScopedValueKey;
bool color;
bool unnatural;
bool positionWithin;
};
typedef std::map<KeyType, AttributeBodyTypes> AttributeBodyTypeMap;
}//IVD

View File

@ -0,0 +1,22 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef ATTRIBUTEPOSITIONPAIR_H
#define ATTRIBUTEPOSITIONPAIR_H
namespace IVD
{
class ReferenceAttributeSet;
struct AttributePositionPair
{
int position;
ReferenceAttributeSet* attrs;
AttributePositionPair() {}
AttributePositionPair(const int p, ReferenceAttributeSet* a): position(p), attrs(a) {}
};
}//IVD
#endif // ATTRIBUTEPOSITIONPAIR_H

View File

@ -0,0 +1,195 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include <string>
#include <optional>
#include <memory>
#include <functional>
#include <sstream>
#include <assert.h>
namespace IVD
{
template<typename Node>
std::string generateExpressionPrintout(const Node& root)
{
//Form the tree
//*___
// |
// ___/___
//| |
//3 ___*____
// | |
// 4 [other.key]
const int minPadding = 3;
const int minWidth = 7;
const int barPadding = 3;
struct ShadowNode
{
std::string literal;
int width;
int centerOffset;
int intraNodePadding;
int barWidth;
std::unique_ptr<ShadowNode> left;
std::unique_ptr<ShadowNode> right;
bool isLeaf() const
{ return !left && !right; }
};
std::function<ShadowNode(const Node&)> createShadowTree = [&](const Node& node) -> ShadowNode
{
ShadowNode shadowNode;
shadowNode.literal = node.printoutThySelf();
if(node.left) shadowNode.left = std::make_unique<ShadowNode>(createShadowTree(*node.left));
if(node.right) shadowNode.right = std::make_unique<ShadowNode>(createShadowTree(*node.right));
assert(node.left && node.right || !node.left && !node.right);
if(shadowNode.isLeaf())
{
if(shadowNode.literal.size() <= minWidth) shadowNode.width = minWidth;
else shadowNode.width = shadowNode.literal.size(); //TODO Make unicode safe (ICU)
shadowNode.intraNodePadding = 0;
shadowNode.centerOffset = shadowNode.width / 2;
}
else
{
const int leftWidth = shadowNode.left->width;
const int leftCenter = shadowNode.left->centerOffset;
const int rightWidth = shadowNode.right->width;
const int rightCenter = shadowNode.right->centerOffset;
const int naturalWidth = leftWidth + rightWidth + minPadding;
if(naturalWidth < minWidth)
{
shadowNode.width = minWidth;
shadowNode.intraNodePadding = minPadding - (leftWidth + rightWidth);
}
else
{
shadowNode.width = naturalWidth;
shadowNode.intraNodePadding = minPadding;
}
shadowNode.barWidth = leftWidth - leftCenter + shadowNode.intraNodePadding + rightCenter;
//Make a quick adjustment for people that think it's cool to have an operator
// longer than 1 character.
const int minBarWidth = barPadding * 2 + shadowNode.literal.size();
if(shadowNode.barWidth < minBarWidth)
{
const int difference = minBarWidth - shadowNode.barWidth;
shadowNode.width += difference;
shadowNode.intraNodePadding += difference;
shadowNode.barWidth = minBarWidth;
}
shadowNode.centerOffset = shadowNode.barWidth / 2 + leftCenter;
}
return shadowNode;
};
const ShadowNode shadowTree = createShadowTree(root);
struct Matrix
{
int x;
int y;
std::vector<std::string> lines;
};
std::vector<Matrix> myMatrices;
std::function<void(const ShadowNode&, const int, const int)> developMatrices =
[&](const ShadowNode& myNode, const int xoffset, const int yoffset) -> void
{
Matrix myMatrix;
myMatrix.x = xoffset;
myMatrix.y = yoffset;
const int nodeYoffset = yoffset + 2;
myMatrix.lines.emplace_back();
std::string& firstLine = myMatrix.lines.back();
if(!myNode.isLeaf())
{
std::string secondLine;
firstLine.append(myNode.centerOffset, ' ');
firstLine.push_back('|');
const int childLeftOffset = myNode.left->centerOffset;
secondLine.append(childLeftOffset, ' ');
secondLine.append(myNode.barWidth + 1, '.'); //+ 1 because truncation
const int opPosition = myNode.centerOffset - myNode.literal.size() / 2;
secondLine.replace(opPosition, myNode.literal.size(), myNode.literal);
myMatrix.lines.push_back(secondLine);
}
else
{
firstLine.append(myNode.centerOffset, ' ');
firstLine.push_back('|');
myMatrix.lines.push_back(myNode.literal);
}
myMatrices.push_back(myMatrix);
if(!myNode.isLeaf())
{
developMatrices(*myNode.left, xoffset, nodeYoffset);
developMatrices(*myNode.right,
xoffset + myNode.left->width + myNode.intraNodePadding,
nodeYoffset);
}
};
developMatrices(shadowTree, 0, 0);
std::vector<std::string> lineBuffer;
//Composite matrices
for(Matrix myMatrix : myMatrices)
{
while(myMatrix.lines.size() + myMatrix.y > lineBuffer.size())
lineBuffer.emplace_back();
for(int i = 0; i != myMatrix.lines.size(); ++i)
{
const int yoffset = myMatrix.y + i;
std::string& outputLine = lineBuffer[yoffset];
std::string& lineSection = myMatrix.lines[i];
const int xoffset = myMatrix.x;
if(lineSection.size() + xoffset > outputLine.size())
outputLine.append(lineSection.size() + xoffset - outputLine.size(), ' ');
outputLine.replace(xoffset, lineSection.size(), lineSection);
}
}
//Finally. :)
std::stringstream printout;
for(const std::string line : lineBuffer) printout << line << std::endl;
return printout.str();
};
}//IVD

92
src/canvas.h Normal file
View File

@ -0,0 +1,92 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef CANVAS_H
#define CANVAS_H
#include <vector>
#include <memory>
#include "geometry.h"
#include "color.h"
namespace IVD
{
class DisplayItem;
struct Bitmap
{
int stride; //Is stride really necessary at this layer of abstraction?
int width;
int height;
int channels;
unsigned char* data;
};
class Canvas
{
protected:
//Okay so I promise this is for a real reason and not because I'm too lazy
// to add clips this to all the drawing functions, which theoretically
// would be "faster".
//The reason is that clips are cumulative, and need to be unwound, and
// children shouldn't know about parent clips.
std::vector<Rect> clips;
Coords offset;
double alpha = 255;
public:
void pushClip(const Rect clip)
{ clips.push_back(clip); }
void popClip()
{ clips.pop_back(); }
void setAlpha(const double theAlpha)
{ alpha = theAlpha; }
double getAlpha()
{ return alpha; }
void setOffset(const Coords theOffset)
{ offset = theOffset; }
void resetOffset()
{ offset = Coords(); }
virtual void setSize(const Dimens size) = 0;
virtual Dimens getSize() = 0;
virtual bool ready() = 0;
virtual void clear() = 0;
virtual void fillRect(Rect r, Color theColor) = 0;
virtual void strokeRect(Rect r, int size, Color theColor, Color::AlphaType alpha) = 0;
virtual void drawLine(Coords start, Coords end, int size, Color theColor, Color::AlphaType alpha) = 0;
virtual void drawGradient(Rect box,
Color toftColor,
Color::AlphaType toftAlpha,
Color boriColor,
Color::AlphaType boriAlpha,
Angle theAngle) = 0;
virtual void drawDropShadow(Rect box, int size, Color theColor, Color::AlphaType alpha) = 0;
virtual void drawBitmapRGBoptionalA(Coords dest,
int stride,
int width,
int height,
int channels,
unsigned char* data) = 0;
virtual void drawText(Coords origin, const std::string text, DisplayItem* style) = 0;
virtual void flush() = 0;
virtual Bitmap getBitmap() = 0;
};
}//IVD
#endif // CANVAS_H

260
src/cbindings.cpp Normal file
View File

@ -0,0 +1,260 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "environment.h"
#include "displayitem.h"
#include "specific_driver_sdl/sdldriver.h"
//I've gone most of my life without using reinterpret_cast, and now look at me.
//Look at what I've become.
//I'm sorry uncle.
//I'm so sorry.
static IVD::Environment* castEnv(IVD_Environment* environment)
{ return reinterpret_cast<IVD::Environment*>(environment); }
static IVD::WidgetWrapper* castWidget(IVD_Widget* widget)
{ return reinterpret_cast<IVD::WidgetWrapper*>(widget); }
static IVD_Widget* castWidget(IVD::WidgetWrapper* widget)
{ return reinterpret_cast<IVD_Widget*>(widget); }
static IVD::Dimens* castDimens(IVD_Dimens* space)
{ return reinterpret_cast<IVD::Dimens*>(space); }
static IVD_Dimens* castDimens(IVD::Dimens* space)
{ return reinterpret_cast<IVD_Dimens*>(space); }
static IVD::Coords* castPoint(IVD_Coords* point)
{ return reinterpret_cast<IVD::Coords*>(point); }
static IVD_Coords* castPoint(IVD::Coords* point)
{ return reinterpret_cast<IVD_Coords*>(point); }
static IVD::DisplayItem* cast(IVD_Element* elem)
{ return reinterpret_cast<IVD::DisplayItem*>(elem); }
static const IVD::DisplayItem* cast(const IVD_Element* elem)
{ return reinterpret_cast<const IVD::DisplayItem*>(elem); }
extern "C"
{
#include "user_include/IVD_c.h"
//--------------------------------------------------------------------------------------Environment
IVD_Environment* IVD_create_environment()
{ return reinterpret_cast<IVD_Environment*>(new IVD::Environment()); }
void IVD_destroy_environment(IVD_Environment* environment)
{ delete castEnv(environment); }
int IVD_environment_load_file(IVD_Environment* environment, const char* path)
{
auto* properEnv = reinterpret_cast<IVD::Environment*>(environment);
return properEnv->loadFromIVDFile(path);
}
const char* IVD_environment_get_compiler_errors(IVD_Environment* environment)
{
auto* properEnv = reinterpret_cast<IVD::Environment*>(environment);
return properEnv->getCompilerErrors();
}
void IVD_environment_run(IVD_Environment* environment)
{
auto* properEnv = reinterpret_cast<IVD::Environment*>(environment);
//And this is where we take control! 🎊
properEnv->run();
}
void IVD_environment_register_widget(IVD_Environment* environment,
const char* name,
IVD_Widget* (*ctor)(IVD_Environment*),
void (*dtor)(IVD_Widget*),
int (*getFillPrecedence)(IVD_Widget*, const int),
void (*shape)(IVD_Widget*, IVD_GeometryProposal*),
void (*draw)(IVD_Widget*, IVD_Canvas*),//canbe null
IVD_Dimens* (*getSpace)(IVD_Widget*),
int (*detectCollisionPoint)(IVD_Widget*, IVD_Coords*), //canbe null
void (*bubbler)(IVD_Widget*),
void (*triggerHandler)(IVD_Widget*, const char*))
{ castEnv(environment)->registerWidgetBlueprints(name, {name, true, ctor, dtor, getFillPrecedence, shape, getSpace, draw, bubbler, detectCollisionPoint, triggerHandler}); }
//IVD manages widget lifetimes so they can be "deleted later"
IVD_Widget* IVD_environment_widget_create(IVD_Environment* environment, const char* name, IVD_Widget* parent)
{ return castEnv(environment)->createWidget(name, parent); }
IVD_Element* IVD_environment_create_element_from_class(IVD_Environment* environment, const char* className, IVD_Widget* parent)
{ return castEnv(environment)->createIVDelementFromClass(className, parent); }
void IVD_environment_widget_destroy(IVD_Environment* environment, IVD_Widget* widget)
{ castEnv(environment)->destroyWidget(widget); }
void IVD_environment_element_destroy(IVD_Environment* environment, IVD_Widget* parent, IVD_Element* elem)
{ castEnv(environment)->destroyIVDelement(parent, elem); }
void IVD_environment_register_layout(IVD_Environment* environment,
const char* name,
IVD_Widget* (*ctor)(IVD_Environment*),
void (*dtor)(IVD_Widget*),
int (*getFillPrecedence)(IVD_Widget*, const int),
void (*shape)(IVD_Widget*, IVD_GeometryProposal*),
void (*draw)(IVD_Widget*, IVD_Canvas*),
IVD_Dimens* (*getSpace)(IVD_Widget*),
void (*bubbler)(IVD_Widget*))
{ castEnv(environment)->registerLayoutBlueprints(name, {name, false, ctor, dtor, getFillPrecedence, shape, getSpace, draw, bubbler, nullptr, nullptr}); }
//----------------------------------------------------------------------------------------------Dust Bindings
IVD_Dimens* IVD_dimens_alloc()
{ return castDimens(new IVD::Dimens()); }
void IVD_dimens_free(IVD_Dimens* space)
{ delete castDimens(space); }
int* IVD_dimens_w(IVD_Dimens* space)
{ return &castDimens(space)->w; }
int* IVD_dimens_h(IVD_Dimens* space)
{ return &castDimens(space)->h; }
IVD_Coords* IVD_coords_alloc()
{ return castPoint(new IVD::Coords()); }
void IVD_coords_free(IVD_Coords* point)
{ delete castPoint(point); }
int* IVD_coords_x(IVD_Coords* point)
{ return &castPoint(point)->x; }
int* IVD_coords_y(IVD_Coords* point)
{ return &castPoint(point)->y; }
static IVD::Rect* castRect(IVD_Rect* rect)
{ return reinterpret_cast<IVD::Rect*>(rect); }
IVD_Rect* IVD_rect_alloc()
{ return reinterpret_cast<IVD_Rect*>(new IVD::Rect()); }
void IVD_rect_free(IVD_Rect* rect)
{ delete castRect(rect); }
IVD_Dimens* IVD_rect_get_dimens(IVD_Rect* rect)
{ return castDimens(&castRect(rect)->d); }
IVD_Coords* IVD_rect_get_coords(IVD_Rect* rect)
{ return castPoint(&castRect(rect)->c); }
void IVD_rect_set_space(IVD_Rect* rect, IVD_Dimens* space)
{ castRect(rect)->d = *castDimens(space); }
void IVD_rect_set_point(IVD_Rect* rect, IVD_Coords* point)
{ castRect(rect)->c = *castPoint(point); }
//-------------------------------------------------------------------------------------------GeometryProposal
static IVD::GeometryProposal* castGeoprop(IVD_GeometryProposal* prop)
{ return reinterpret_cast<IVD::GeometryProposal*>(prop); }
IVD_GeometryProposal* IVD_geoprop_alloc()
{ return reinterpret_cast<IVD_GeometryProposal*>(new IVD::GeometryProposal()); }
void IVD_geoprop_free(IVD_GeometryProposal* prop)
{ delete castGeoprop(prop); }
IVD_Dimens* IVD_geoprop_proposed_space(IVD_GeometryProposal* prop)
{ return castDimens(&castGeoprop(prop)->proposedDimensions); }
int* IVD_geoprop_expand_horizontal(IVD_GeometryProposal* prop)
{ return &castGeoprop(prop)->expandForAngle(IVD::Angle::Horizontal); }
int* IVD_geoprop_expand_vertical(IVD_GeometryProposal* prop)
{ return &castGeoprop(prop)->shrinkForAngle(IVD::Angle::Vertical); }
int* IVD_geoprop_shrink_horizontal(IVD_GeometryProposal* prop)
{ return &castGeoprop(prop)->shrinkForAngle(IVD::Angle::Horizontal); }
int* IVD_geoprop_shrink_vertical(IVD_GeometryProposal* prop)
{ return &castGeoprop(prop)->shrinkForAngle(IVD::Angle::Vertical); }
int IVD_geoprop_verify_compliance(IVD_GeometryProposal* prop, IVD_Dimens* space)
{ return castGeoprop(prop)->verifyCompliance(*castDimens(space)); }
void IVD_geoprop_round_conflicts(IVD_GeometryProposal* prop, IVD_Dimens* space)
{ *castDimens(space) = castGeoprop(prop)->roundConflicts(*castDimens(space)); }
//-----------------------------------------------------------------------------------------------------Widget
IVD_Dimens* IVD_element_get_dimens(const IVD_Element* elem)
{
thread_local static IVD::Dimens dimens;
dimens = cast(elem)->getViewportDimens();
return castDimens(&dimens);
}
int IVD_element_get_fill_precedence(IVD_Element* elem, int angle)
{
const auto fillPrec =
cast(elem)->computerFillPrecedenceForAngle(static_cast<IVD::Angle>(angle));
return static_cast<int>(fillPrec);
}
void IVD_element_shape(IVD_Element* elem, IVD_GeometryProposal* prop)
{ cast(elem)->shape(prop); }
void IVD_element_set_offset(IVD_Element* elem, IVD_Coords* coords)
{ cast(elem)->setOffset(coords); }
void IVD_element_draw(IVD_Element* elem)
{ cast(elem)->render(); }
void IVD_element_bubble(IVD_Element* elem)
{ cast(elem)->updateHover(); }
IVD_Element* IVD_widget_get_underlying_element(IVD_Environment* environment, IVD_Widget* widget)
{ return reinterpret_cast<IVD_Element*>(castEnv(environment)->getUnderlyingDisplayItemForWidget(widget)); }
//On requiring widget instead of parent element here.
// ehhhh it doesn't make a whole lot of difference.
//But this does kind of prevent you from going deeper into the
// tree than one level (you can't recursively get child elements)
// which I kind of like.
void IVD_widget_get_child_elements(IVD_Environment* environment, IVD_Widget* widget, IVD_Element*** result, int* size)
{
thread_local static std::vector<IVD_Element*> resultContainer;
IVD::DisplayItem* item = castEnv(environment)->getUnderlyingDisplayItemForWidget(widget);
resultContainer = item->getChildWidgetInStampOrder();
*size = resultContainer.size();
*result = &resultContainer[0];
}
IVD_Element* IVD_widget_get_child_element_for_named_cell(IVD_Environment* environment, IVD_Widget* parent, const char* name)
{
IVD::DisplayItem* item = castEnv(environment)->getUnderlyingDisplayItemForWidget(parent);
return item->getChildElementForNamedCell(name);
}
//--------------------Accessors
void IVD_canvas_draw_image(IVD_Canvas* canvas,
int x,
int y,
int width,
int height,
int stride,
int channels,
unsigned char* data)
{
IVD::Canvas* myCanvas = reinterpret_cast<IVD::Canvas*>(canvas);
//absolute drawing offset is already set by the
// owning DisplayItem, the x,y here are relative
myCanvas->drawBitmapRGBoptionalA(IVD::Coords(x,y),
stride,
width,
height,
channels,
data);
}
}//extern "C"

18
src/codeposition.h Normal file
View File

@ -0,0 +1,18 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include "rustutils/lexcompare.h"
namespace IVD
{
struct CodePosition
{
int line;
int column;
RUSTUTILS_DEFINE_COMP(CodePosition, line, column)
};
}//IVD

817
src/color.cpp Normal file
View File

@ -0,0 +1,817 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "color.h"
#include <sstream>
#include <iomanip>
std::string IVD::Color::generateHexPrint() const
{
auto convertColor = [&](uint8_t theColor) -> std::string
{
std::stringstream miniStream;
miniStream << std::setfill('0') << std::setw(2) << std::hex << (int)theColor;
return miniStream.str();
};
std::string hexedColor = "#";
hexedColor += convertColor(red);
hexedColor += convertColor(green);
hexedColor += convertColor(blue);
return hexedColor;
}
std::string IVD::Color::generateDecPrint() const
{
std::stringstream stream;
stream << (int)red << ", " << (int)green << ", " << (int)blue;
return stream.str();
}
IVD::Color::Color(const std::string& key): red(0), green(0), blue(0)
{
if(key == "snow") *this = Color(255, 250, 250);
if(key == "ghost white") *this = Color(248, 248, 255);
if(key == "GhostWhite") *this = Color(248, 248, 255);
if(key == "white smoke") *this = Color(245, 245, 245);
if(key == "WhiteSmoke") *this = Color(245, 245, 245);
if(key == "gainsboro") *this = Color(220, 220, 220);
if(key == "floral white") *this = Color(255, 250, 240);
if(key == "FloralWhite") *this = Color(255, 250, 240);
if(key == "old lace") *this = Color(253, 245, 230);
if(key == "OldLace") *this = Color(253, 245, 230);
if(key == "linen") *this = Color(250, 240, 230);
if(key == "antique white") *this = Color(250, 235, 215);
if(key == "AntiqueWhite") *this = Color(250, 235, 215);
if(key == "papaya whip") *this = Color(255, 239, 213);
if(key == "PapayaWhip") *this = Color(255, 239, 213);
if(key == "blanched almond") *this = Color(255, 235, 205);
if(key == "BlanchedAlmond") *this = Color(255, 235, 205);
if(key == "bisque") *this = Color(255, 228, 196);
if(key == "peach puff") *this = Color(255, 218, 185);
if(key == "PeachPuff") *this = Color(255, 218, 185);
if(key == "navajo white") *this = Color(255, 222, 173);
if(key == "NavajoWhite") *this = Color(255, 222, 173);
if(key == "moccasin") *this = Color(255, 228, 181);
if(key == "cornsilk") *this = Color(255, 248, 220);
if(key == "ivory") *this = Color(255, 255, 240);
if(key == "lemon chiffon") *this = Color(255, 250, 205);
if(key == "LemonChiffon") *this = Color(255, 250, 205);
if(key == "seashell") *this = Color(255, 245, 238);
if(key == "honeydew") *this = Color(240, 255, 240);
if(key == "mint cream") *this = Color(245, 255, 250);
if(key == "MintCream") *this = Color(245, 255, 250);
if(key == "azure") *this = Color(240, 255, 255);
if(key == "alice blue") *this = Color(240, 248, 255);
if(key == "AliceBlue") *this = Color(240, 248, 255);
if(key == "lavender") *this = Color(230, 230, 250);
if(key == "lavender blush") *this = Color(255, 240, 245);
if(key == "LavenderBlush") *this = Color(255, 240, 245);
if(key == "misty rose") *this = Color(255, 228, 225);
if(key == "MistyRose") *this = Color(255, 228, 225);
if(key == "white") *this = Color(255, 255, 255);
if(key == "black") *this = Color(0, 0, 0);
if(key == "dark slate gray") *this = Color(47, 79, 79);
if(key == "DarkSlateGray") *this = Color(47, 79, 79);
if(key == "dark slate grey") *this = Color(47, 79, 79);
if(key == "DarkSlateGrey") *this = Color(47, 79, 79);
if(key == "dim gray") *this = Color(105, 105, 105);
if(key == "DimGray") *this = Color(105, 105, 105);
if(key == "dim grey") *this = Color(105, 105, 105);
if(key == "DimGrey") *this = Color(105, 105, 105);
if(key == "slate gray") *this = Color(112, 128, 144);
if(key == "SlateGray") *this = Color(112, 128, 144);
if(key == "slate grey") *this = Color(112, 128, 144);
if(key == "SlateGrey") *this = Color(112, 128, 144);
if(key == "light slate gray") *this = Color(119, 136, 153);
if(key == "LightSlateGray") *this = Color(119, 136, 153);
if(key == "light slate grey") *this = Color(119, 136, 153);
if(key == "LightSlateGrey") *this = Color(119, 136, 153);
if(key == "gray") *this = Color(190, 190, 190);
if(key == "grey") *this = Color(190, 190, 190);
if(key == "x11 gray") *this = Color(190, 190, 190);
if(key == "X11Gray") *this = Color(190, 190, 190);
if(key == "x11 grey") *this = Color(190, 190, 190);
if(key == "X11Grey") *this = Color(190, 190, 190);
if(key == "web gray") *this = Color(128, 128, 128);
if(key == "WebGray") *this = Color(128, 128, 128);
if(key == "web grey") *this = Color(128, 128, 128);
if(key == "WebGrey") *this = Color(128, 128, 128);
if(key == "light grey") *this = Color(211, 211, 211);
if(key == "LightGrey") *this = Color(211, 211, 211);
if(key == "light gray") *this = Color(211, 211, 211);
if(key == "LightGray") *this = Color(211, 211, 211);
if(key == "midnight blue") *this = Color(25, 25, 112);
if(key == "MidnightBlue") *this = Color(25, 25, 112);
if(key == "navy") *this = Color(0, 0, 128);
if(key == "navy blue") *this = Color(0, 0, 128);
if(key == "NavyBlue") *this = Color(0, 0, 128);
if(key == "cornflower blue") *this = Color(100, 149, 237);
if(key == "CornflowerBlue") *this = Color(100, 149, 237);
if(key == "dark slate blue") *this = Color(72, 61, 139);
if(key == "DarkSlateBlue") *this = Color(72, 61, 139);
if(key == "slate blue") *this = Color(106, 90, 205);
if(key == "SlateBlue") *this = Color(106, 90, 205);
if(key == "medium slate blue") *this = Color(123, 104, 238);
if(key == "MediumSlateBlue") *this = Color(123, 104, 238);
if(key == "light slate blue") *this = Color(132, 112, 255);
if(key == "LightSlateBlue") *this = Color(132, 112, 255);
if(key == "medium blue") *this = Color(0, 0, 205);
if(key == "MediumBlue") *this = Color(0, 0, 205);
if(key == "royal blue") *this = Color(65, 105, 225);
if(key == "RoyalBlue") *this = Color(65, 105, 225);
if(key == "blue") *this = Color(0, 0, 255);
if(key == "dodger blue") *this = Color(30, 144, 255);
if(key == "DodgerBlue") *this = Color(30, 144, 255);
if(key == "deep sky blue") *this = Color(0, 191, 255);
if(key == "DeepSkyBlue") *this = Color(0, 191, 255);
if(key == "sky blue") *this = Color(135, 206, 235);
if(key == "SkyBlue") *this = Color(135, 206, 235);
if(key == "light sky blue") *this = Color(135, 206, 250);
if(key == "LightSkyBlue") *this = Color(135, 206, 250);
if(key == "steel blue") *this = Color(70, 130, 180);
if(key == "SteelBlue") *this = Color(70, 130, 180);
if(key == "light steel blue") *this = Color(176, 196, 222);
if(key == "LightSteelBlue") *this = Color(176, 196, 222);
if(key == "light blue") *this = Color(173, 216, 230);
if(key == "LightBlue") *this = Color(173, 216, 230);
if(key == "powder blue") *this = Color(176, 224, 230);
if(key == "PowderBlue") *this = Color(176, 224, 230);
if(key == "pale turquoise") *this = Color(175, 238, 238);
if(key == "PaleTurquoise") *this = Color(175, 238, 238);
if(key == "dark turquoise") *this = Color(0, 206, 209);
if(key == "DarkTurquoise") *this = Color(0, 206, 209);
if(key == "medium turquoise") *this = Color(72, 209, 204);
if(key == "MediumTurquoise") *this = Color(72, 209, 204);
if(key == "turquoise") *this = Color(64, 224, 208);
if(key == "cyan") *this = Color(0, 255, 255);
if(key == "aqua") *this = Color(0, 255, 255);
if(key == "light cyan") *this = Color(224, 255, 255);
if(key == "LightCyan") *this = Color(224, 255, 255);
if(key == "cadet blue") *this = Color(95, 158, 160);
if(key == "CadetBlue") *this = Color(95, 158, 160);
if(key == "medium aquamarine") *this = Color(102, 205, 170);
if(key == "MediumAquamarine") *this = Color(102, 205, 170);
if(key == "aquamarine") *this = Color(127, 255, 212);
if(key == "dark green") *this = Color(0, 100, 0);
if(key == "DarkGreen") *this = Color(0, 100, 0);
if(key == "dark olive green") *this = Color(85, 107, 47);
if(key == "DarkOliveGreen") *this = Color(85, 107, 47);
if(key == "dark sea green") *this = Color(143, 188, 143);
if(key == "DarkSeaGreen") *this = Color(143, 188, 143);
if(key == "sea green") *this = Color(46, 139, 87);
if(key == "SeaGreen") *this = Color(46, 139, 87);
if(key == "medium sea green") *this = Color(60, 179, 113);
if(key == "MediumSeaGreen") *this = Color(60, 179, 113);
if(key == "light sea green") *this = Color(32, 178, 170);
if(key == "LightSeaGreen") *this = Color(32, 178, 170);
if(key == "pale green") *this = Color(152, 251, 152);
if(key == "PaleGreen") *this = Color(152, 251, 152);
if(key == "spring green") *this = Color(0, 255, 127);
if(key == "SpringGreen") *this = Color(0, 255, 127);
if(key == "lawn green") *this = Color(124, 252, 0);
if(key == "LawnGreen") *this = Color(124, 252, 0);
if(key == "green") *this = Color(0, 255, 0);
if(key == "lime") *this = Color(0, 255, 0);
if(key == "x11 green") *this = Color(0, 255, 0);
if(key == "X11Green") *this = Color(0, 255, 0);
if(key == "web green") *this = Color(0, 128, 0);
if(key == "WebGreen") *this = Color(0, 128, 0);
if(key == "chartreuse") *this = Color(127, 255, 0);
if(key == "medium spring green") *this = Color(0, 250, 154);
if(key == "MediumSpringGreen") *this = Color(0, 250, 154);
if(key == "green yellow") *this = Color(173, 255, 47);
if(key == "GreenYellow") *this = Color(173, 255, 47);
if(key == "lime green") *this = Color(50, 205, 50);
if(key == "LimeGreen") *this = Color(50, 205, 50);
if(key == "yellow green") *this = Color(154, 205, 50);
if(key == "YellowGreen") *this = Color(154, 205, 50);
if(key == "forest green") *this = Color(34, 139, 34);
if(key == "ForestGreen") *this = Color(34, 139, 34);
if(key == "olive drab") *this = Color(107, 142, 35);
if(key == "OliveDrab") *this = Color(107, 142, 35);
if(key == "dark khaki") *this = Color(189, 183, 107);
if(key == "DarkKhaki") *this = Color(189, 183, 107);
if(key == "khaki") *this = Color(240, 230, 140);
if(key == "pale goldenrod") *this = Color(238, 232, 170);
if(key == "PaleGoldenrod") *this = Color(238, 232, 170);
if(key == "light goldenrod yellow") *this = Color(250, 250, 210);
if(key == "LightGoldenrodYellow") *this = Color(250, 250, 210);
if(key == "light yellow") *this = Color(255, 255, 224);
if(key == "LightYellow") *this = Color(255, 255, 224);
if(key == "yellow") *this = Color(255, 255, 0);
if(key == "gold") *this = Color(255, 215, 0);
if(key == "light goldenrod") *this = Color(238, 221, 130);
if(key == "LightGoldenrod") *this = Color(238, 221, 130);
if(key == "goldenrod") *this = Color(218, 165, 32);
if(key == "dark goldenrod") *this = Color(184, 134, 11);
if(key == "DarkGoldenrod") *this = Color(184, 134, 11);
if(key == "rosy brown") *this = Color(188, 143, 143);
if(key == "RosyBrown") *this = Color(188, 143, 143);
if(key == "indian red") *this = Color(205, 92, 92);
if(key == "IndianRed") *this = Color(205, 92, 92);
if(key == "saddle brown") *this = Color(139, 69, 19);
if(key == "SaddleBrown") *this = Color(139, 69, 19);
if(key == "sienna") *this = Color(160, 82, 45);
if(key == "peru") *this = Color(205, 133, 63);
if(key == "burlywood") *this = Color(222, 184, 135);
if(key == "beige") *this = Color(245, 245, 220);
if(key == "wheat") *this = Color(245, 222, 179);
if(key == "sandy brown") *this = Color(244, 164, 96);
if(key == "SandyBrown") *this = Color(244, 164, 96);
if(key == "tan") *this = Color(210, 180, 140);
if(key == "chocolate") *this = Color(210, 105, 30);
if(key == "firebrick") *this = Color(178, 34, 34);
if(key == "brown") *this = Color(165, 42, 42);
if(key == "dark salmon") *this = Color(233, 150, 122);
if(key == "DarkSalmon") *this = Color(233, 150, 122);
if(key == "salmon") *this = Color(250, 128, 114);
if(key == "light salmon") *this = Color(255, 160, 122);
if(key == "LightSalmon") *this = Color(255, 160, 122);
if(key == "orange") *this = Color(255, 165, 0);
if(key == "dark orange") *this = Color(255, 140, 0);
if(key == "DarkOrange") *this = Color(255, 140, 0);
if(key == "coral") *this = Color(255, 127, 80);
if(key == "light coral") *this = Color(240, 128, 128);
if(key == "LightCoral") *this = Color(240, 128, 128);
if(key == "tomato") *this = Color(255, 99, 71);
if(key == "orange red") *this = Color(255, 69, 0);
if(key == "OrangeRed") *this = Color(255, 69, 0);
if(key == "red") *this = Color(255, 0, 0);
if(key == "hot pink") *this = Color(255, 105, 180);
if(key == "HotPink") *this = Color(255, 105, 180);
if(key == "deep pink") *this = Color(255, 20, 147);
if(key == "DeepPink") *this = Color(255, 20, 147);
if(key == "pink") *this = Color(255, 192, 203);
if(key == "light pink") *this = Color(255, 182, 193);
if(key == "LightPink") *this = Color(255, 182, 193);
if(key == "pale violet red") *this = Color(219, 112, 147);
if(key == "PaleVioletRed") *this = Color(219, 112, 147);
if(key == "maroon") *this = Color(176, 48, 96);
if(key == "x11 maroon") *this = Color(176, 48, 96);
if(key == "X11Maroon") *this = Color(176, 48, 96);
if(key == "web maroon") *this = Color(128, 0, 0);
if(key == "WebMaroon") *this = Color(128, 0, 0);
if(key == "medium violet red") *this = Color(199, 21, 133);
if(key == "MediumVioletRed") *this = Color(199, 21, 133);
if(key == "violet red") *this = Color(208, 32, 144);
if(key == "VioletRed") *this = Color(208, 32, 144);
if(key == "magenta") *this = Color(255, 0, 255);
if(key == "fuchsia") *this = Color(255, 0, 255);
if(key == "violet") *this = Color(238, 130, 238);
if(key == "plum") *this = Color(221, 160, 221);
if(key == "orchid") *this = Color(218, 112, 214);
if(key == "medium orchid") *this = Color(186, 85, 211);
if(key == "MediumOrchid") *this = Color(186, 85, 211);
if(key == "dark orchid") *this = Color(153, 50, 204);
if(key == "DarkOrchid") *this = Color(153, 50, 204);
if(key == "dark violet") *this = Color(148, 0, 211);
if(key == "DarkViolet") *this = Color(148, 0, 211);
if(key == "blue violet") *this = Color(138, 43, 226);
if(key == "BlueViolet") *this = Color(138, 43, 226);
if(key == "purple") *this = Color(160, 32, 240);
if(key == "x11 purple") *this = Color(160, 32, 240);
if(key == "X11Purple") *this = Color(160, 32, 240);
if(key == "web purple") *this = Color(128, 0, 128);
if(key == "WebPurple") *this = Color(128, 0, 128);
if(key == "medium purple") *this = Color(147, 112, 219);
if(key == "MediumPurple") *this = Color(147, 112, 219);
if(key == "thistle") *this = Color(216, 191, 216);
if(key == "snow1") *this = Color(255, 250, 250);
if(key == "snow2") *this = Color(238, 233, 233);
if(key == "snow3") *this = Color(205, 201, 201);
if(key == "snow4") *this = Color(139, 137, 137);
if(key == "seashell1") *this = Color(255, 245, 238);
if(key == "seashell2") *this = Color(238, 229, 222);
if(key == "seashell3") *this = Color(205, 197, 191);
if(key == "seashell4") *this = Color(139, 134, 130);
if(key == "AntiqueWhite1") *this = Color(255, 239, 219);
if(key == "AntiqueWhite2") *this = Color(238, 223, 204);
if(key == "AntiqueWhite3") *this = Color(205, 192, 176);
if(key == "AntiqueWhite4") *this = Color(139, 131, 120);
if(key == "bisque1") *this = Color(255, 228, 196);
if(key == "bisque2") *this = Color(238, 213, 183);
if(key == "bisque3") *this = Color(205, 183, 158);
if(key == "bisque4") *this = Color(139, 125, 107);
if(key == "PeachPuff1") *this = Color(255, 218, 185);
if(key == "PeachPuff2") *this = Color(238, 203, 173);
if(key == "PeachPuff3") *this = Color(205, 175, 149);
if(key == "PeachPuff4") *this = Color(139, 119, 101);
if(key == "NavajoWhite1") *this = Color(255, 222, 173);
if(key == "NavajoWhite2") *this = Color(238, 207, 161);
if(key == "NavajoWhite3") *this = Color(205, 179, 139);
if(key == "NavajoWhite4") *this = Color(139, 121, 94);
if(key == "LemonChiffon1") *this = Color(255, 250, 205);
if(key == "LemonChiffon2") *this = Color(238, 233, 191);
if(key == "LemonChiffon3") *this = Color(205, 201, 165);
if(key == "LemonChiffon4") *this = Color(139, 137, 112);
if(key == "cornsilk1") *this = Color(255, 248, 220);
if(key == "cornsilk2") *this = Color(238, 232, 205);
if(key == "cornsilk3") *this = Color(205, 200, 177);
if(key == "cornsilk4") *this = Color(139, 136, 120);
if(key == "ivory1") *this = Color(255, 255, 240);
if(key == "ivory2") *this = Color(238, 238, 224);
if(key == "ivory3") *this = Color(205, 205, 193);
if(key == "ivory4") *this = Color(139, 139, 131);
if(key == "honeydew1") *this = Color(240, 255, 240);
if(key == "honeydew2") *this = Color(224, 238, 224);
if(key == "honeydew3") *this = Color(193, 205, 193);
if(key == "honeydew4") *this = Color(131, 139, 131);
if(key == "LavenderBlush1") *this = Color(255, 240, 245);
if(key == "LavenderBlush2") *this = Color(238, 224, 229);
if(key == "LavenderBlush3") *this = Color(205, 193, 197);
if(key == "LavenderBlush4") *this = Color(139, 131, 134);
if(key == "MistyRose1") *this = Color(255, 228, 225);
if(key == "MistyRose2") *this = Color(238, 213, 210);
if(key == "MistyRose3") *this = Color(205, 183, 181);
if(key == "MistyRose4") *this = Color(139, 125, 123);
if(key == "azure1") *this = Color(240, 255, 255);
if(key == "azure2") *this = Color(224, 238, 238);
if(key == "azure3") *this = Color(193, 205, 205);
if(key == "azure4") *this = Color(131, 139, 139);
if(key == "SlateBlue1") *this = Color(131, 111, 255);
if(key == "SlateBlue2") *this = Color(122, 103, 238);
if(key == "SlateBlue3") *this = Color(105, 89, 205);
if(key == "SlateBlue4") *this = Color(71, 60, 139);
if(key == "RoyalBlue1") *this = Color(72, 118, 255);
if(key == "RoyalBlue2") *this = Color(67, 110, 238);
if(key == "RoyalBlue3") *this = Color(58, 95, 205);
if(key == "RoyalBlue4") *this = Color(39, 64, 139);
if(key == "blue1") *this = Color(0, 0, 255);
if(key == "blue2") *this = Color(0, 0, 238);
if(key == "blue3") *this = Color(0, 0, 205);
if(key == "blue4") *this = Color(0, 0, 139);
if(key == "DodgerBlue1") *this = Color(30, 144, 255);
if(key == "DodgerBlue2") *this = Color(28, 134, 238);
if(key == "DodgerBlue3") *this = Color(24, 116, 205);
if(key == "DodgerBlue4") *this = Color(16, 78, 139);
if(key == "SteelBlue1") *this = Color(99, 184, 255);
if(key == "SteelBlue2") *this = Color(92, 172, 238);
if(key == "SteelBlue3") *this = Color(79, 148, 205);
if(key == "SteelBlue4") *this = Color(54, 100, 139);
if(key == "DeepSkyBlue1") *this = Color(0, 191, 255);
if(key == "DeepSkyBlue2") *this = Color(0, 178, 238);
if(key == "DeepSkyBlue3") *this = Color(0, 154, 205);
if(key == "DeepSkyBlue4") *this = Color(0, 104, 139);
if(key == "SkyBlue1") *this = Color(135, 206, 255);
if(key == "SkyBlue2") *this = Color(126, 192, 238);
if(key == "SkyBlue3") *this = Color(108, 166, 205);
if(key == "SkyBlue4") *this = Color(74, 112, 139);
if(key == "LightSkyBlue1") *this = Color(176, 226, 255);
if(key == "LightSkyBlue2") *this = Color(164, 211, 238);
if(key == "LightSkyBlue3") *this = Color(141, 182, 205);
if(key == "LightSkyBlue4") *this = Color(96, 123, 139);
if(key == "SlateGray1") *this = Color(198, 226, 255);
if(key == "SlateGray2") *this = Color(185, 211, 238);
if(key == "SlateGray3") *this = Color(159, 182, 205);
if(key == "SlateGray4") *this = Color(108, 123, 139);
if(key == "LightSteelBlue1") *this = Color(202, 225, 255);
if(key == "LightSteelBlue2") *this = Color(188, 210, 238);
if(key == "LightSteelBlue3") *this = Color(162, 181, 205);
if(key == "LightSteelBlue4") *this = Color(110, 123, 139);
if(key == "LightBlue1") *this = Color(191, 239, 255);
if(key == "LightBlue2") *this = Color(178, 223, 238);
if(key == "LightBlue3") *this = Color(154, 192, 205);
if(key == "LightBlue4") *this = Color(104, 131, 139);
if(key == "LightCyan1") *this = Color(224, 255, 255);
if(key == "LightCyan2") *this = Color(209, 238, 238);
if(key == "LightCyan3") *this = Color(180, 205, 205);
if(key == "LightCyan4") *this = Color(122, 139, 139);
if(key == "PaleTurquoise1") *this = Color(187, 255, 255);
if(key == "PaleTurquoise2") *this = Color(174, 238, 238);
if(key == "PaleTurquoise3") *this = Color(150, 205, 205);
if(key == "PaleTurquoise4") *this = Color(102, 139, 139);
if(key == "CadetBlue1") *this = Color(152, 245, 255);
if(key == "CadetBlue2") *this = Color(142, 229, 238);
if(key == "CadetBlue3") *this = Color(122, 197, 205);
if(key == "CadetBlue4") *this = Color(83, 134, 139);
if(key == "turquoise1") *this = Color(0, 245, 255);
if(key == "turquoise2") *this = Color(0, 229, 238);
if(key == "turquoise3") *this = Color(0, 197, 205);
if(key == "turquoise4") *this = Color(0, 134, 139);
if(key == "cyan1") *this = Color(0, 255, 255);
if(key == "cyan2") *this = Color(0, 238, 238);
if(key == "cyan3") *this = Color(0, 205, 205);
if(key == "cyan4") *this = Color(0, 139, 139);
if(key == "DarkSlateGray1") *this = Color(151, 255, 255);
if(key == "DarkSlateGray2") *this = Color(141, 238, 238);
if(key == "DarkSlateGray3") *this = Color(121, 205, 205);
if(key == "DarkSlateGray4") *this = Color(82, 139, 139);
if(key == "aquamarine1") *this = Color(127, 255, 212);
if(key == "aquamarine2") *this = Color(118, 238, 198);
if(key == "aquamarine3") *this = Color(102, 205, 170);
if(key == "aquamarine4") *this = Color(69, 139, 116);
if(key == "DarkSeaGreen1") *this = Color(193, 255, 193);
if(key == "DarkSeaGreen2") *this = Color(180, 238, 180);
if(key == "DarkSeaGreen3") *this = Color(155, 205, 155);
if(key == "DarkSeaGreen4") *this = Color(105, 139, 105);
if(key == "SeaGreen1") *this = Color(84, 255, 159);
if(key == "SeaGreen2") *this = Color(78, 238, 148);
if(key == "SeaGreen3") *this = Color(67, 205, 128);
if(key == "SeaGreen4") *this = Color(46, 139, 87);
if(key == "PaleGreen1") *this = Color(154, 255, 154);
if(key == "PaleGreen2") *this = Color(144, 238, 144);
if(key == "PaleGreen3") *this = Color(124, 205, 124);
if(key == "PaleGreen4") *this = Color(84, 139, 84);
if(key == "SpringGreen1") *this = Color(0, 255, 127);
if(key == "SpringGreen2") *this = Color(0, 238, 118);
if(key == "SpringGreen3") *this = Color(0, 205, 102);
if(key == "SpringGreen4") *this = Color(0, 139, 69);
if(key == "green1") *this = Color(0, 255, 0);
if(key == "green2") *this = Color(0, 238, 0);
if(key == "green3") *this = Color(0, 205, 0);
if(key == "green4") *this = Color(0, 139, 0);
if(key == "chartreuse1") *this = Color(127, 255, 0);
if(key == "chartreuse2") *this = Color(118, 238, 0);
if(key == "chartreuse3") *this = Color(102, 205, 0);
if(key == "chartreuse4") *this = Color(69, 139, 0);
if(key == "OliveDrab1") *this = Color(192, 255, 62);
if(key == "OliveDrab2") *this = Color(179, 238, 58);
if(key == "OliveDrab3") *this = Color(154, 205, 50);
if(key == "OliveDrab4") *this = Color(105, 139, 34);
if(key == "DarkOliveGreen1") *this = Color(202, 255, 112);
if(key == "DarkOliveGreen2") *this = Color(188, 238, 104);
if(key == "DarkOliveGreen3") *this = Color(162, 205, 90);
if(key == "DarkOliveGreen4") *this = Color(110, 139, 61);
if(key == "khaki1") *this = Color(255, 246, 143);
if(key == "khaki2") *this = Color(238, 230, 133);
if(key == "khaki3") *this = Color(205, 198, 115);
if(key == "khaki4") *this = Color(139, 134, 78);
if(key == "LightGoldenrod1") *this = Color(255, 236, 139);
if(key == "LightGoldenrod2") *this = Color(238, 220, 130);
if(key == "LightGoldenrod3") *this = Color(205, 190, 112);
if(key == "LightGoldenrod4") *this = Color(139, 129, 76);
if(key == "LightYellow1") *this = Color(255, 255, 224);
if(key == "LightYellow2") *this = Color(238, 238, 209);
if(key == "LightYellow3") *this = Color(205, 205, 180);
if(key == "LightYellow4") *this = Color(139, 139, 122);
if(key == "yellow1") *this = Color(255, 255, 0);
if(key == "yellow2") *this = Color(238, 238, 0);
if(key == "yellow3") *this = Color(205, 205, 0);
if(key == "yellow4") *this = Color(139, 139, 0);
if(key == "gold1") *this = Color(255, 215, 0);
if(key == "gold2") *this = Color(238, 201, 0);
if(key == "gold3") *this = Color(205, 173, 0);
if(key == "gold4") *this = Color(139, 117, 0);
if(key == "goldenrod1") *this = Color(255, 193, 37);
if(key == "goldenrod2") *this = Color(238, 180, 34);
if(key == "goldenrod3") *this = Color(205, 155, 29);
if(key == "goldenrod4") *this = Color(139, 105, 20);
if(key == "DarkGoldenrod1") *this = Color(255, 185, 15);
if(key == "DarkGoldenrod2") *this = Color(238, 173, 14);
if(key == "DarkGoldenrod3") *this = Color(205, 149, 12);
if(key == "DarkGoldenrod4") *this = Color(139, 101, 8);
if(key == "RosyBrown1") *this = Color(255, 193, 193);
if(key == "RosyBrown2") *this = Color(238, 180, 180);
if(key == "RosyBrown3") *this = Color(205, 155, 155);
if(key == "RosyBrown4") *this = Color(139, 105, 105);
if(key == "IndianRed1") *this = Color(255, 106, 106);
if(key == "IndianRed2") *this = Color(238, 99, 99);
if(key == "IndianRed3") *this = Color(205, 85, 85);
if(key == "IndianRed4") *this = Color(139, 58, 58);
if(key == "sienna1") *this = Color(255, 130, 71);
if(key == "sienna2") *this = Color(238, 121, 66);
if(key == "sienna3") *this = Color(205, 104, 57);
if(key == "sienna4") *this = Color(139, 71, 38);
if(key == "burlywood1") *this = Color(255, 211, 155);
if(key == "burlywood2") *this = Color(238, 197, 145);
if(key == "burlywood3") *this = Color(205, 170, 125);
if(key == "burlywood4") *this = Color(139, 115, 85);
if(key == "wheat1") *this = Color(255, 231, 186);
if(key == "wheat2") *this = Color(238, 216, 174);
if(key == "wheat3") *this = Color(205, 186, 150);
if(key == "wheat4") *this = Color(139, 126, 102);
if(key == "tan1") *this = Color(255, 165, 79);
if(key == "tan2") *this = Color(238, 154, 73);
if(key == "tan3") *this = Color(205, 133, 63);
if(key == "tan4") *this = Color(139, 90, 43);
if(key == "chocolate1") *this = Color(255, 127, 36);
if(key == "chocolate2") *this = Color(238, 118, 33);
if(key == "chocolate3") *this = Color(205, 102, 29);
if(key == "chocolate4") *this = Color(139, 69, 19);
if(key == "firebrick1") *this = Color(255, 48, 48);
if(key == "firebrick2") *this = Color(238, 44, 44);
if(key == "firebrick3") *this = Color(205, 38, 38);
if(key == "firebrick4") *this = Color(139, 26, 26);
if(key == "brown1") *this = Color(255, 64, 64);
if(key == "brown2") *this = Color(238, 59, 59);
if(key == "brown3") *this = Color(205, 51, 51);
if(key == "brown4") *this = Color(139, 35, 35);
if(key == "salmon1") *this = Color(255, 140, 105);
if(key == "salmon2") *this = Color(238, 130, 98);
if(key == "salmon3") *this = Color(205, 112, 84);
if(key == "salmon4") *this = Color(139, 76, 57);
if(key == "LightSalmon1") *this = Color(255, 160, 122);
if(key == "LightSalmon2") *this = Color(238, 149, 114);
if(key == "LightSalmon3") *this = Color(205, 129, 98);
if(key == "LightSalmon4") *this = Color(139, 87, 66);
if(key == "orange1") *this = Color(255, 165, 0);
if(key == "orange2") *this = Color(238, 154, 0);
if(key == "orange3") *this = Color(205, 133, 0);
if(key == "orange4") *this = Color(139, 90, 0);
if(key == "DarkOrange1") *this = Color(255, 127, 0);
if(key == "DarkOrange2") *this = Color(238, 118, 0);
if(key == "DarkOrange3") *this = Color(205, 102, 0);
if(key == "DarkOrange4") *this = Color(139, 69, 0);
if(key == "coral1") *this = Color(255, 114, 86);
if(key == "coral2") *this = Color(238, 106, 80);
if(key == "coral3") *this = Color(205, 91, 69);
if(key == "coral4") *this = Color(139, 62, 47);
if(key == "tomato1") *this = Color(255, 99, 71);
if(key == "tomato2") *this = Color(238, 92, 66);
if(key == "tomato3") *this = Color(205, 79, 57);
if(key == "tomato4") *this = Color(139, 54, 38);
if(key == "OrangeRed1") *this = Color(255, 69, 0);
if(key == "OrangeRed2") *this = Color(238, 64, 0);
if(key == "OrangeRed3") *this = Color(205, 55, 0);
if(key == "OrangeRed4") *this = Color(139, 37, 0);
if(key == "red1") *this = Color(255, 0, 0);
if(key == "red2") *this = Color(238, 0, 0);
if(key == "red3") *this = Color(205, 0, 0);
if(key == "red4") *this = Color(139, 0, 0);
if(key == "DeepPink1") *this = Color(255, 20, 147);
if(key == "DeepPink2") *this = Color(238, 18, 137);
if(key == "DeepPink3") *this = Color(205, 16, 118);
if(key == "DeepPink4") *this = Color(139, 10, 80);
if(key == "HotPink1") *this = Color(255, 110, 180);
if(key == "HotPink2") *this = Color(238, 106, 167);
if(key == "HotPink3") *this = Color(205, 96, 144);
if(key == "HotPink4") *this = Color(139, 58, 98);
if(key == "pink1") *this = Color(255, 181, 197);
if(key == "pink2") *this = Color(238, 169, 184);
if(key == "pink3") *this = Color(205, 145, 158);
if(key == "pink4") *this = Color(139, 99, 108);
if(key == "LightPink1") *this = Color(255, 174, 185);
if(key == "LightPink2") *this = Color(238, 162, 173);
if(key == "LightPink3") *this = Color(205, 140, 149);
if(key == "LightPink4") *this = Color(139, 95, 101);
if(key == "PaleVioletRed1") *this = Color(255, 130, 171);
if(key == "PaleVioletRed2") *this = Color(238, 121, 159);
if(key == "PaleVioletRed3") *this = Color(205, 104, 137);
if(key == "PaleVioletRed4") *this = Color(139, 71, 93);
if(key == "maroon1") *this = Color(255, 52, 179);
if(key == "maroon2") *this = Color(238, 48, 167);
if(key == "maroon3") *this = Color(205, 41, 144);
if(key == "maroon4") *this = Color(139, 28, 98);
if(key == "VioletRed1") *this = Color(255, 62, 150);
if(key == "VioletRed2") *this = Color(238, 58, 140);
if(key == "VioletRed3") *this = Color(205, 50, 120);
if(key == "VioletRed4") *this = Color(139, 34, 82);
if(key == "magenta1") *this = Color(255, 0, 255);
if(key == "magenta2") *this = Color(238, 0, 238);
if(key == "magenta3") *this = Color(205, 0, 205);
if(key == "magenta4") *this = Color(139, 0, 139);
if(key == "orchid1") *this = Color(255, 131, 250);
if(key == "orchid2") *this = Color(238, 122, 233);
if(key == "orchid3") *this = Color(205, 105, 201);
if(key == "orchid4") *this = Color(139, 71, 137);
if(key == "plum1") *this = Color(255, 187, 255);
if(key == "plum2") *this = Color(238, 174, 238);
if(key == "plum3") *this = Color(205, 150, 205);
if(key == "plum4") *this = Color(139, 102, 139);
if(key == "MediumOrchid1") *this = Color(224, 102, 255);
if(key == "MediumOrchid2") *this = Color(209, 95, 238);
if(key == "MediumOrchid3") *this = Color(180, 82, 205);
if(key == "MediumOrchid4") *this = Color(122, 55, 139);
if(key == "DarkOrchid1") *this = Color(191, 62, 255);
if(key == "DarkOrchid2") *this = Color(178, 58, 238);
if(key == "DarkOrchid3") *this = Color(154, 50, 205);
if(key == "DarkOrchid4") *this = Color(104, 34, 139);
if(key == "purple1") *this = Color(155, 48, 255);
if(key == "purple2") *this = Color(145, 44, 238);
if(key == "purple3") *this = Color(125, 38, 205);
if(key == "purple4") *this = Color(85, 26, 139);
if(key == "MediumPurple1") *this = Color(171, 130, 255);
if(key == "MediumPurple2") *this = Color(159, 121, 238);
if(key == "MediumPurple3") *this = Color(137, 104, 205);
if(key == "MediumPurple4") *this = Color(93, 71, 139);
if(key == "thistle1") *this = Color(255, 225, 255);
if(key == "thistle2") *this = Color(238, 210, 238);
if(key == "thistle3") *this = Color(205, 181, 205);
if(key == "thistle4") *this = Color(139, 123, 139);
if(key == "gray0") *this = Color(0, 0, 0);
if(key == "grey0") *this = Color(0, 0, 0);
if(key == "gray1") *this = Color(3, 3, 3);
if(key == "grey1") *this = Color(3, 3, 3);
if(key == "gray2") *this = Color(5, 5, 5);
if(key == "grey2") *this = Color(5, 5, 5);
if(key == "gray3") *this = Color(8, 8, 8);
if(key == "grey3") *this = Color(8, 8, 8);
if(key == "gray4") *this = Color(10, 10, 10);
if(key == "grey4") *this = Color(10, 10, 10);
if(key == "gray5") *this = Color(13, 13, 13);
if(key == "grey5") *this = Color(13, 13, 13);
if(key == "gray6") *this = Color(15, 15, 15);
if(key == "grey6") *this = Color(15, 15, 15);
if(key == "gray7") *this = Color(18, 18, 18);
if(key == "grey7") *this = Color(18, 18, 18);
if(key == "gray8") *this = Color(20, 20, 20);
if(key == "grey8") *this = Color(20, 20, 20);
if(key == "gray9") *this = Color(23, 23, 23);
if(key == "grey9") *this = Color(23, 23, 23);
if(key == "gray10") *this = Color(26, 26, 26);
if(key == "grey10") *this = Color(26, 26, 26);
if(key == "gray11") *this = Color(28, 28, 28);
if(key == "grey11") *this = Color(28, 28, 28);
if(key == "gray12") *this = Color(31, 31, 31);
if(key == "grey12") *this = Color(31, 31, 31);
if(key == "gray13") *this = Color(33, 33, 33);
if(key == "grey13") *this = Color(33, 33, 33);
if(key == "gray14") *this = Color(36, 36, 36);
if(key == "grey14") *this = Color(36, 36, 36);
if(key == "gray15") *this = Color(38, 38, 38);
if(key == "grey15") *this = Color(38, 38, 38);
if(key == "gray16") *this = Color(41, 41, 41);
if(key == "grey16") *this = Color(41, 41, 41);
if(key == "gray17") *this = Color(43, 43, 43);
if(key == "grey17") *this = Color(43, 43, 43);
if(key == "gray18") *this = Color(46, 46, 46);
if(key == "grey18") *this = Color(46, 46, 46);
if(key == "gray19") *this = Color(48, 48, 48);
if(key == "grey19") *this = Color(48, 48, 48);
if(key == "gray20") *this = Color(51, 51, 51);
if(key == "grey20") *this = Color(51, 51, 51);
if(key == "gray21") *this = Color(54, 54, 54);
if(key == "grey21") *this = Color(54, 54, 54);
if(key == "gray22") *this = Color(56, 56, 56);
if(key == "grey22") *this = Color(56, 56, 56);
if(key == "gray23") *this = Color(59, 59, 59);
if(key == "grey23") *this = Color(59, 59, 59);
if(key == "gray24") *this = Color(61, 61, 61);
if(key == "grey24") *this = Color(61, 61, 61);
if(key == "gray25") *this = Color(64, 64, 64);
if(key == "grey25") *this = Color(64, 64, 64);
if(key == "gray26") *this = Color(66, 66, 66);
if(key == "grey26") *this = Color(66, 66, 66);
if(key == "gray27") *this = Color(69, 69, 69);
if(key == "grey27") *this = Color(69, 69, 69);
if(key == "gray28") *this = Color(71, 71, 71);
if(key == "grey28") *this = Color(71, 71, 71);
if(key == "gray29") *this = Color(74, 74, 74);
if(key == "grey29") *this = Color(74, 74, 74);
if(key == "gray30") *this = Color(77, 77, 77);
if(key == "grey30") *this = Color(77, 77, 77);
if(key == "gray31") *this = Color(79, 79, 79);
if(key == "grey31") *this = Color(79, 79, 79);
if(key == "gray32") *this = Color(82, 82, 82);
if(key == "grey32") *this = Color(82, 82, 82);
if(key == "gray33") *this = Color(84, 84, 84);
if(key == "grey33") *this = Color(84, 84, 84);
if(key == "gray34") *this = Color(87, 87, 87);
if(key == "grey34") *this = Color(87, 87, 87);
if(key == "gray35") *this = Color(89, 89, 89);
if(key == "grey35") *this = Color(89, 89, 89);
if(key == "gray36") *this = Color(92, 92, 92);
if(key == "grey36") *this = Color(92, 92, 92);
if(key == "gray37") *this = Color(94, 94, 94);
if(key == "grey37") *this = Color(94, 94, 94);
if(key == "gray38") *this = Color(97, 97, 97);
if(key == "grey38") *this = Color(97, 97, 97);
if(key == "gray39") *this = Color(99, 99, 99);
if(key == "grey39") *this = Color(99, 99, 99);
if(key == "gray40") *this = Color(102, 102, 102);
if(key == "grey40") *this = Color(102, 102, 102);
if(key == "gray41") *this = Color(105, 105, 105);
if(key == "grey41") *this = Color(105, 105, 105);
if(key == "gray42") *this = Color(107, 107, 107);
if(key == "grey42") *this = Color(107, 107, 107);
if(key == "gray43") *this = Color(110, 110, 110);
if(key == "grey43") *this = Color(110, 110, 110);
if(key == "gray44") *this = Color(112, 112, 112);
if(key == "grey44") *this = Color(112, 112, 112);
if(key == "gray45") *this = Color(115, 115, 115);
if(key == "grey45") *this = Color(115, 115, 115);
if(key == "gray46") *this = Color(117, 117, 117);
if(key == "grey46") *this = Color(117, 117, 117);
if(key == "gray47") *this = Color(120, 120, 120);
if(key == "grey47") *this = Color(120, 120, 120);
if(key == "gray48") *this = Color(122, 122, 122);
if(key == "grey48") *this = Color(122, 122, 122);
if(key == "gray49") *this = Color(125, 125, 125);
if(key == "grey49") *this = Color(125, 125, 125);
if(key == "gray50") *this = Color(127, 127, 127);
if(key == "grey50") *this = Color(127, 127, 127);
if(key == "gray51") *this = Color(130, 130, 130);
if(key == "grey51") *this = Color(130, 130, 130);
if(key == "gray52") *this = Color(133, 133, 133);
if(key == "grey52") *this = Color(133, 133, 133);
if(key == "gray53") *this = Color(135, 135, 135);
if(key == "grey53") *this = Color(135, 135, 135);
if(key == "gray54") *this = Color(138, 138, 138);
if(key == "grey54") *this = Color(138, 138, 138);
if(key == "gray55") *this = Color(140, 140, 140);
if(key == "grey55") *this = Color(140, 140, 140);
if(key == "gray56") *this = Color(143, 143, 143);
if(key == "grey56") *this = Color(143, 143, 143);
if(key == "gray57") *this = Color(145, 145, 145);
if(key == "grey57") *this = Color(145, 145, 145);
if(key == "gray58") *this = Color(148, 148, 148);
if(key == "grey58") *this = Color(148, 148, 148);
if(key == "gray59") *this = Color(150, 150, 150);
if(key == "grey59") *this = Color(150, 150, 150);
if(key == "gray60") *this = Color(153, 153, 153);
if(key == "grey60") *this = Color(153, 153, 153);
if(key == "gray61") *this = Color(156, 156, 156);
if(key == "grey61") *this = Color(156, 156, 156);
if(key == "gray62") *this = Color(158, 158, 158);
if(key == "grey62") *this = Color(158, 158, 158);
if(key == "gray63") *this = Color(161, 161, 161);
if(key == "grey63") *this = Color(161, 161, 161);
if(key == "gray64") *this = Color(163, 163, 163);
if(key == "grey64") *this = Color(163, 163, 163);
if(key == "gray65") *this = Color(166, 166, 166);
if(key == "grey65") *this = Color(166, 166, 166);
if(key == "gray66") *this = Color(168, 168, 168);
if(key == "grey66") *this = Color(168, 168, 168);
if(key == "gray67") *this = Color(171, 171, 171);
if(key == "grey67") *this = Color(171, 171, 171);
if(key == "gray68") *this = Color(173, 173, 173);
if(key == "grey68") *this = Color(173, 173, 173);
if(key == "gray69") *this = Color(176, 176, 176);
if(key == "grey69") *this = Color(176, 176, 176);
if(key == "gray70") *this = Color(179, 179, 179);
if(key == "grey70") *this = Color(179, 179, 179);
if(key == "gray71") *this = Color(181, 181, 181);
if(key == "grey71") *this = Color(181, 181, 181);
if(key == "gray72") *this = Color(184, 184, 184);
if(key == "grey72") *this = Color(184, 184, 184);
if(key == "gray73") *this = Color(186, 186, 186);
if(key == "grey73") *this = Color(186, 186, 186);
if(key == "gray74") *this = Color(189, 189, 189);
if(key == "grey74") *this = Color(189, 189, 189);
if(key == "gray75") *this = Color(191, 191, 191);
if(key == "grey75") *this = Color(191, 191, 191);
if(key == "gray76") *this = Color(194, 194, 194);
if(key == "grey76") *this = Color(194, 194, 194);
if(key == "gray77") *this = Color(196, 196, 196);
if(key == "grey77") *this = Color(196, 196, 196);
if(key == "gray78") *this = Color(199, 199, 199);
if(key == "grey78") *this = Color(199, 199, 199);
if(key == "gray79") *this = Color(201, 201, 201);
if(key == "grey79") *this = Color(201, 201, 201);
if(key == "gray80") *this = Color(204, 204, 204);
if(key == "grey80") *this = Color(204, 204, 204);
if(key == "gray81") *this = Color(207, 207, 207);
if(key == "grey81") *this = Color(207, 207, 207);
if(key == "gray82") *this = Color(209, 209, 209);
if(key == "grey82") *this = Color(209, 209, 209);
if(key == "gray83") *this = Color(212, 212, 212);
if(key == "grey83") *this = Color(212, 212, 212);
if(key == "gray84") *this = Color(214, 214, 214);
if(key == "grey84") *this = Color(214, 214, 214);
if(key == "gray85") *this = Color(217, 217, 217);
if(key == "grey85") *this = Color(217, 217, 217);
if(key == "gray86") *this = Color(219, 219, 219);
if(key == "grey86") *this = Color(219, 219, 219);
if(key == "gray87") *this = Color(222, 222, 222);
if(key == "grey87") *this = Color(222, 222, 222);
if(key == "gray88") *this = Color(224, 224, 224);
if(key == "grey88") *this = Color(224, 224, 224);
if(key == "gray89") *this = Color(227, 227, 227);
if(key == "grey89") *this = Color(227, 227, 227);
if(key == "gray90") *this = Color(229, 229, 229);
if(key == "grey90") *this = Color(229, 229, 229);
if(key == "gray91") *this = Color(232, 232, 232);
if(key == "grey91") *this = Color(232, 232, 232);
if(key == "gray92") *this = Color(235, 235, 235);
if(key == "grey92") *this = Color(235, 235, 235);
if(key == "gray93") *this = Color(237, 237, 237);
if(key == "grey93") *this = Color(237, 237, 237);
if(key == "gray94") *this = Color(240, 240, 240);
if(key == "grey94") *this = Color(240, 240, 240);
if(key == "gray95") *this = Color(242, 242, 242);
if(key == "grey95") *this = Color(242, 242, 242);
if(key == "gray96") *this = Color(245, 245, 245);
if(key == "grey96") *this = Color(245, 245, 245);
if(key == "gray97") *this = Color(247, 247, 247);
if(key == "grey97") *this = Color(247, 247, 247);
if(key == "gray98") *this = Color(250, 250, 250);
if(key == "grey98") *this = Color(250, 250, 250);
if(key == "gray99") *this = Color(252, 252, 252);
if(key == "grey99") *this = Color(252, 252, 252);
if(key == "gray100") *this = Color(255, 255, 255);
if(key == "grey100") *this = Color(255, 255, 255);
if(key == "dark grey") *this = Color(169, 169, 169);
if(key == "DarkGrey") *this = Color(169, 169, 169);
if(key == "dark gray") *this = Color(169, 169, 169);
if(key == "DarkGray") *this = Color(169, 169, 169);
if(key == "dark blue") *this = Color(0, 0, 139);
if(key == "DarkBlue") *this = Color(0, 0, 139);
if(key == "dark cyan") *this = Color(0, 139, 139);
if(key == "DarkCyan") *this = Color(0, 139, 139);
if(key == "dark magenta") *this = Color(139, 0, 139);
if(key == "DarkMagenta") *this = Color(139, 0, 139);
if(key == "dark red") *this = Color(139, 0, 0);
if(key == "DarkRed") *this = Color(139, 0, 0);
if(key == "light green") *this = Color(144, 238, 144);
if(key == "LightGreen") *this = Color(144, 238, 144);
if(key == "crimson") *this = Color(220, 20, 60);
if(key == "indigo") *this = Color(75, 0, 130);
if(key == "olive") *this = Color(128, 128, 0);
if(key == "rebecca purple") *this = Color(102, 51, 153);
if(key == "RebeccaPurple") *this = Color(102, 51, 153);
if(key == "silver") *this = Color(192, 192, 192);
if(key == "teal") *this = Color(0, 128, 128);
}

50
src/color.h Normal file
View File

@ -0,0 +1,50 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef COLOR_H
#define COLOR_H
#include <cstdint>
#include <string>
#include "rustutils/lexcompare.h"
namespace IVD
{
struct Color
{
//For "DeepColor", probably should just switch channel
// to floating point so the (0,255, 5), #ffffff, etc
// stuff translates easily. Could also be rounded off
// for legacy hardware.
typedef uint8_t Channel;
typedef Channel AlphaType;
Channel red;
Channel green;
Channel blue;
std::string generateHexPrint() const;
std::string generateDecPrint() const;
Color() {}
Color(Channel r, Channel g, Channel b):
red(r), green(g), blue(b)
{}
Color(const std::string& key);
RUSTUTILS_DEFINE_COMP(Color, red, green, blue)
};
struct GradientColorStop
{
Color myColor;
Color::AlphaType myAlpha;
double pos;
};
}//IVD
#endif // COLOR_H

2042
src/compiler.cpp Executable file

File diff suppressed because it is too large Load Diff

265
src/compiler.h Executable file
View File

@ -0,0 +1,265 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef COMPILER_H
#define COMPILER_H
#include <list>
#include "element.h"
#include "graph.h"
#include "codeposition.h"
#include "attributebodytypes.h"
#include <sstream>
namespace IVD
{
struct ScopedVariables
{
DefineContainer defines;
std::map<ValueKey, Animation::Graph> graphs;
};
struct Token
{
CodePosition codePosition;
const int symbol;
std::string userToken;
std::string userString;
double userNumber;
Color userColor;
Token(const CodePosition codePosition, const int sym):
codePosition(codePosition),
symbol(sym)
{}
};
class UnexpectedEOF : public std::exception {};
class SyntaxError : public std::exception
{
const CodePosition pos;
//I understand that std::string can throw, but this error object is
// only thrown on syntax errors, not exactly a likely time for
// an std::string to throw... The problem is that I want to construct
// error messages conditionally and c strings won't work.
//We could store the state to regenerate them but ehhh...
std::optional<std::string> errStr;
struct Expecting
{
int expected;
int got;
};
std::optional<Expecting> expecting;
std::optional<CodePosition> conflictsWith;
public:
SyntaxError(const Token theTok, const int expected):
pos(theTok.codePosition),
expecting({expected, theTok.symbol})
{}
SyntaxError(const Token theTok, const std::string errStr):
pos(theTok.codePosition),
errStr(errStr)
{}
SyntaxError(const CodePosition pos): pos(pos) {}
SyntaxError(const CodePosition pos, const std::string errStr):
pos(pos),
errStr(errStr)
{}
SyntaxError(const std::string errStr,
const CodePosition pos,
const CodePosition conflictsWith):
errStr(errStr),
pos(pos),
conflictsWith(conflictsWith)
{}
std::string printout(const char* context) const;
};
class Compiler
{
int lastElementStamp;
std::vector<std::string> errorMessages;
std::string publicErrorBuffer;
//Warnings?
std::map<std::string, CodePosition> elementNamePositions;
std::list<Element> finalizedElements;
std::map<std::string, int> tokenToSymbolMap;
std::map<int, std::string> symbolToTokenMap;
AttributeBodyTypeMap attrKeyToBodyTypeMap;
const std::set<int> delimitingSymbolSet;
std::map<int, std::vector<int>> unnaturalKeyMap;
std::map<int, std::set<int>> validPropertySetMap;
enum class ElementType
{
IsClass,
IsRemora,
IsInstance,
};
struct UserToken
{
CodePosition pos;
std::string value;
UserToken& operator=(const Token& tok)
{
pos = tok.codePosition;
value = tok.userToken;
return *this;
}
};
struct ElementPrecursor
{
CodePosition codePosition;
int stamp;
int lastVirtualStateStamp;
Element elem;
ElementType myType;
std::string name;
UserToken hostName;
ScopedValueKey currentState; //Doesn't need to be here.
std::vector<UserToken> myClasses;
ScopedValueKey myModel;
std::vector<ElementPrecursor> remoras;
ScopedVariables vars;
ElementPrecursor(const CodePosition codePosition, const int stamp, const int attrCount):
codePosition(codePosition),
stamp(stamp),
lastVirtualStateStamp(0),
elem(stamp, attrCount),
currentState(),
myModel()
{}
int getFreshVirtualStateStamp()
{ return lastVirtualStateStamp++; }
void autoGenerateRandomName()
{
std::stringstream ss;
std::stringstream generatedName;
generatedName << "anonymous-" << stamp << "-HORRIBLY-MAANGLED";
name = generatedName.str();
}
};
bool checkSymbolIsNaturalAttributeKey(const int sym)
{
const AttributeBodyTypes types = attrKeyToBodyTypeMap.at(sym);
return !types.unnatural;
}
int getKeyspaceSize()
{ return tokenToSymbolMap.size(); }
int getFreshElementStamp()
{ return lastElementStamp++; }
std::map<std::string, ElementPrecursor> myClasses;
//The idea is that the classes can persist across
// multiple source files, but elements cannot.
//And that elements can be accessed directly by
// the user, but classes cannot.
std::vector<SyntaxError> myErrors; //I mean, they're the user's, but...
void postSyntaxError(const SyntaxError& err) { myErrors.push_back(err); }
void nameConflict(const std::string err,
const CodePosition pos1,
const CodePosition pos2)
{ postSyntaxError(SyntaxError(err.c_str(), pos1, pos2)); }
std::vector<Token> tokenizeInput(const std::string code);
std::vector<ElementPrecursor> parseTokens(std::vector<Token> tokens);
void finalizePrecursors(std::vector<ElementPrecursor> precursors);
bool checkIsDelimitingSymbol(const int sym)
{ return delimitingSymbolSet.count(sym); }
int getSymbolKeyspaceSize()
{ return tokenToSymbolMap.size(); }
public:
Compiler():
lastElementStamp(0),
tokenToSymbolMap(getTokenToSymbolMap()),
symbolToTokenMap(getSymbolToTokenMap()),
attrKeyToBodyTypeMap(getStandardAttributes()),
delimitingSymbolSet(getDelimitingSymbolSet()),
unnaturalKeyMap(getNaturalKeysToUnnaturalKeyMap()),
validPropertySetMap(getAttributeKeyToValidPropertyList())
{}
bool compileFile(const char* path);
void compile(std::string code);
std::optional<int> getSymbolForLiteral(const std::string token)
{
std::optional<int> result;
auto it = tokenToSymbolMap.find(token);
if(it != tokenToSymbolMap.end())
result = it->second;
return result;
}
std::string getLiteralForSymbol(const int sym)
{
auto it = symbolToTokenMap.find(sym);
if(it == symbolToTokenMap.end())
return "UNDEFINED (todo lol)";
return it->second;
}
//Would love for this to be const
// but I don't have all day TODO
std::list<Element>& getElements()
{ return finalizedElements; }
Element* getElementForClass(const std::string className)
{
if(myClasses.count(className))
return &myClasses.at(className).elem;
return nullptr;
}
std::vector<std::string> getErrorMessages()
{ return errorMessages; }
const std::string& getErrorMessageDigest();
};
}//IVD
#endif // COMPILER_H

42
src/corefonts.h Normal file
View File

@ -0,0 +1,42 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
//Sans
extern const unsigned char corefontsans[];
extern const int corefontsansSize;
extern const unsigned char corefontsansbold[];
extern const int corefontsansboldSize;
extern const unsigned char corefontsansitalic[];
extern const int corefontsansitalicSize;
extern const unsigned char corefontsansbolditalic[];
extern const int corefontsansbolditalicSize;
//Serif
extern const unsigned char corefontserif[];
extern const int corefontserifSize;
extern const unsigned char corefontserifbold[];
extern const int corefontserifboldSize;
extern const unsigned char corefontserifitalic[];
extern const int corefontserifitalicSize;
extern const unsigned char corefontserifbolditalic[];
extern const int corefontserifbolditalicSize;
//Mono
extern const unsigned char corefontmono[];
extern const int corefontmonoSize;
extern const unsigned char corefontmonobold[];
extern const int corefontmonoboldSize;
extern const unsigned char corefontmonoitalic[];
extern const int corefontmonoitalicSize;
extern const unsigned char corefontmonobolditalic[];
extern const int corefontmonobolditalicSize;

258
src/defaults.cpp Normal file
View File

@ -0,0 +1,258 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "defaults.h"
#include "displayitem.h"
#include "environment.h"
#include "corefonts.h"
#include <fstream>
#include <iostream>
namespace IVD
{
namespace Default
{
namespace Filter
{
int getFontSize(DisplayItem* item)
{
const auto optional = item->getAttr().getInt(AttributeKey::FontSize);
return optional ? *optional
: Default::FontSize;
}
FontData loadCustomFont(const std::string path)
{
//TODO thread-safety? Just lock it if we ever need that. No point in storing
// two copies of the same font, defeating the purpose of the cache, and no
// problem waiting on the other thread to load it, when that's all we'd do anyway.
//Memory mapping would be better.
static std::map<std::string, std::vector<unsigned char>> cachedFonts;
if(cachedFonts.count(path)) return FontData(&cachedFonts[path][0], cachedFonts[path].size());
//Load the fugger...
std::ifstream myFile;
myFile.open(path.c_str(), std::ios::binary);
if(!myFile)
{
std::cerr << "IVD Runtime: Could not open custom font: " << path << std::endl
<< "Falling back on \"sans\"..." << std::endl;
return FontData(corefontsans, corefontsansSize);
}
std::vector<unsigned char> data((std::istreambuf_iterator<char>(myFile)),
std::istreambuf_iterator<char>());
cachedFonts[path] = data;
return FontData(&cachedFonts[path][0], cachedFonts[path].size());
}
FontData getCoreFont(const int fontProperty)
{
switch(fontProperty)
{
case Property::FontSans: return FontData(corefontsans, corefontsansSize);
case Property::FontSansBold: return FontData(corefontsansbold, corefontsansboldSize);
case Property::FontSansItalic: return FontData(corefontsansitalic, corefontsansitalicSize);
case Property::FontSansBoldItalic: return FontData(corefontsansbolditalic, corefontsansbolditalicSize);
case Property::FontSerif: return FontData(corefontserif, corefontserifSize);
case Property::FontSerifBold: return FontData(corefontserifbold, corefontserifboldSize);
case Property::FontSerifItalic: return FontData(corefontserifitalic, corefontserifitalicSize);
case Property::FontSerifBoldItalic: return FontData(corefontserifbolditalic, corefontserifbolditalicSize);
case Property::FontMono: return FontData(corefontmono, corefontmonoSize);
case Property::FontMonoBold: return FontData(corefontmonobold, corefontmonoboldSize);
case Property::FontMonoItalic: return FontData(corefontmonoitalic, corefontmonoitalicSize);
case Property::FontMonoBoldItalic: return FontData(corefontmonobolditalic, corefontmonobolditalicSize);
default: assert(false);
}
}
FontData getFontFace(DisplayItem* item)
{
const auto optionalCustomFont = item->getAttr().getUserToken(AttributeKey::Font);
if(optionalCustomFont) return loadCustomFont(*optionalCustomFont);
const auto optionalFontProperty = item->getAttr().getProperty(AttributeKey::Font);
return getCoreFont(optionalFontProperty ? *optionalFontProperty
: FontFace);
}
static std::string simpleStringFilter(DisplayItem* item, const int key, const std::string def)
{
auto tt = item->getAttr().getUserToken(key);
if(tt) return *tt;
auto vk = item->getAttr().getSingleValueKey(key);
if(vk) return item->getEnv()->getString(item, *vk);
return def;
}
std::string getTitleText(DisplayItem* item)
{
return simpleStringFilter(item, AttributeKey::TitleText, TitleText);
}
std::string getText(DisplayItem* item)
{
return simpleStringFilter(item, AttributeKey::Text, "");
}
Color getFontColor(DisplayItem* item)
{
const auto optional = item->getAttr().getColor(AttributeKey::FontColor);
return optional ? *optional
: FontColor;
}
double getAlpha(DisplayItem* item)
{
return DefaultAlphaFactor;
}
std::optional<Color> getElementColor(DisplayItem* item)
{
//Whether it's set or not is important. No color is different from
// alpha.
return item->getAttr().getColor(AttributeKey::ElementColor);
}
bool getVisibility(DisplayItem* item)
{
auto vis = item->getAttr().getProperty(AttributeKey::Visibility);
if(vis) return *vis == Property::Enable;
return visible;
}
bool getFullscreen(DisplayItem* item)
{
return fullscreen;
}
bool checkResizable(DisplayItem* item)
{
//TODO: Also not resizable if fullscreen... But that should be handled by the driver?
auto transAdj = getSizeAdjacent(item);
auto transOpp = getSizeOpposite(item);
if(transAdj || transOpp) return false;
auto flag = item->getAttr().getProperty(AttributeKey::Resizable);
if(!flag)
{
const int sizeStrategy = getWindowSizeStrategy(item);
if(sizeStrategy == Property::BottomUp)
return false;
else return true;
}
return *flag == Property::Enable;
}
bool checkModelOrder(DisplayItem* item)
{
auto optional = item->getAttr().getProperty(AttributeKey::ModelOrder);
if(!optional) return modelOrder;
return *optional;
}
std::optional<int> getSizeAdjacent(DisplayItem* item)
{
return item->getAttr().getInt(AttributeKey::SizeA);
}
std::optional<int> getSizeOpposite(DisplayItem* item)
{
return item->getAttr().getInt(AttributeKey::SizeO);
}
std::optional<ValueKeyPath> getPositionWithin(DisplayItem* item)
{
if(item->getAttr().getSingleValueKey(AttributeKey::PositionWithin))
{
auto posWith = item->getAttr().getSingleValueKey(AttributeKey::PositionWithin);
auto optionalPath = posWith->path;
auto optionalKey = posWith->key;
if(optionalPath)
return *optionalPath;
return ValueKeyPath({*posWith->key});
}
return std::optional<ValueKeyPath>();
}
std::optional<int> getAdjacentAlignmentProperty(DisplayItem* item)
{
return item->getAttr().getProperty(AttributeKey::AlignAdjacent);
}
std::optional<int> getOppositeAlignmentProperty(DisplayItem* item)
{
return item->getAttr().getProperty(AttributeKey::AlignOpposite);
}
Angle getItemFlowAngle(DisplayItem* item)
{
return Angle::Horizontal; //Ehem...
}
Angle getRowFlowAngle(DisplayItem* item)
{
return Angle::Vertical; //EHEM...
}
int getWindowSizeStrategy(DisplayItem* item)
{
//Also top-down if fullscreen... TODO
const auto primary = item->getAttr().getProperty(AttributeKey::WindowSizeStrategy);
if(primary) return *primary;
return WindowSizeStrategy;
}
std::optional<int> getHorizontalWindowAlignmentProperty(DisplayItem* item)
{
assert(false);
//return item->getAttr().getProperty(AttributeKey::InitialWindowAlignAdj);
}
std::optional<int> getVerticalWindowAlignmentProperty(DisplayItem* item)
{
assert(false);
//return item->getAttr().getProperty(AttributeKey::InitialWindowAlignOpp);
}
std::optional<int> getJustificationProperty(DisplayItem* item)
{
return item->getAttr().getProperty(AttributeKey::Justify);
}
}//Filter
}//Default
}//IVD

104
src/defaults.h Normal file
View File

@ -0,0 +1,104 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef DEFAULTS_H
#define DEFAULTS_H
#include "color.h"
#include "geometry.h"
#include "valuekey.h"
#include "keywords.h"
#include "rustutils/lexcompare.h"
namespace IVD
{
class DisplayItem;
namespace Default
{
//Prefer Default::Filter::get*(DisplayItem*)
// to the literals.
const int FontFace = Property::FontSans;
const int FontSize = 12;
const Color FontColor = Color(0, 0, 0); //Paint it black
const Color ElementColor = Color(255, 255, 255);
const double DefaultAlphaFactor = 1;
const std::string TitleText = "IVD Window";
const ValueKey MousePath = "IVD-Mouse";
const bool visible = true;
const bool fullscreen = false;
const bool resizable = true;
const bool modelOrder = true;
const std::string quitTrigger = "IVD-Core-Quit";
const Angle angle = Angle::Adjacent;
const int alignmentProperty = Property::Inner;
const int WindowSizeStrategy = Property::TopDown;
namespace Filter
{
int getFontSize(DisplayItem* item);
struct FontData
{
const unsigned char* data;
const int size;
FontData(const unsigned char* data, const int size):
data(data),
size(size)
{}
RUSTUTILS_DEFINE_COMP(FontData, data, size);
};
FontData getFontFace(DisplayItem* item);
std::string getTitleText(DisplayItem* item);
std::string getText(DisplayItem* item);
//Alignment getAlignment(DisplayItem* item, const int attrkey);
Color getFontColor(DisplayItem* item);
double getAlpha(DisplayItem* item);
std::optional<Color> getElementColor(DisplayItem* item);
bool getVisibility(DisplayItem* item);
bool getFullscreen(DisplayItem* item);
bool checkResizable(DisplayItem* item);
bool checkModelOrder(DisplayItem* item);
Angle getItemFlowAngle(DisplayItem* item);
Angle getRowFlowAngle(DisplayItem* item);
std::optional<int> getSizeAdjacent(DisplayItem* item);
std::optional<int> getSizeOpposite(DisplayItem* item);
std::optional<ValueKeyPath> getPositionWithin(DisplayItem* item);
//Why are these optional??
std::optional<int> getAdjacentAlignmentProperty(DisplayItem* item);
std::optional<int> getOppositeAlignmentProperty(DisplayItem* item);
std::optional<int> getJustificationProperty(DisplayItem* item);
std::optional<int> getHorizontalWindowAlignmentProperty(DisplayItem* item);
std::optional<int> getVerticalWindowAlignmentProperty(DisplayItem* item);
int getWindowSizeStrategy(DisplayItem* item);
}//Filter
}//Default
}//IVD
#endif // DEFAULTS_H

469
src/displayitem.cpp Executable file
View File

@ -0,0 +1,469 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "displayitem.h"
#include "environment.h"
#include "defaults.h"
#include "canvas.h"
#include "states.h"
#include "assert.h"
namespace IVD
{
Angle DisplayItem::getRelativeAdjacent()
{
auto property = getAttr().getProperty(AttributeKey::Orientation);
return !property ? Default::angle
: *property == Property::AdjacentIsHorizontal ? Angle::Horizontal
: Angle::Vertical;
}
Angle DisplayItem::correctAngle(const Angle theAngle)
{
//Convert SYMANTIC adjacent into RELATIVE adjacent.
return theAngle == Angle::Adjacent ? getRelativeAdjacent()
: getRelativeOpposite();
}
int DisplayItem::getReservedForKey(Angle theAngle, const int adjacentKey, const int oppositeKey)
{
auto key = getAttr().getInt(theAngle == getRelativeAdjacent() ? adjacentKey
: oppositeKey);
return key ? *key
: 0;
}
void DisplayItem::addAttributeSet(const AttributePositionPair pair)
{
contributingAttrs[pair.position] = pair.attrs;
myEnv->markAsChangedAttributes(this);
}
void DisplayItem::removeAttributeSet(const AttributePositionPair pair)
{
contributingAttrs.erase(pair.position);
myEnv->markAsChangedAttributes(this);
}
void DisplayItem::recomputeAttributeSet()
{
myAttrs.beginAttributeSetRecompute();
myAttrs.mergeIn(defaultState);
for(auto pair : contributingAttrs)
myAttrs.mergeIn(*pair.second);
myAttrs.commitAttributeSetRecompute();
}
void DisplayItem::setParent(DisplayItem* item)
{
if(item == parent) return;
deparent();
parent = item;
parent->children.insert(this);
}
void DisplayItem::deparent()
{
if(parent)
{
parent->children.erase(this);
parent = nullptr;
}
}
void DisplayItem::prepareToDie()
{
deparent();
for(DisplayItem* child : children) child->deparent();
}
//The root of the tree is *this* if no parents.
DisplayItem* DisplayItem::getRoot()
{
DisplayItem* root;
DisplayItem* candidate = this;
while(candidate)
{
root = candidate;
candidate = candidate->parent;
}
return root;
}
int DisplayItem::getDepth()
{
if(parent) return parent->getDepth() + 1;
return 0;
}
GeometryProposal DisplayItem::reviseProposalAsRequired(GeometryProposal prop)
{
auto internal = [&](const Angle theAngle)
{
const int attrKey = (theAngle == getRelativeAdjacent()) ? AttributeKey::SizeA
: AttributeKey::SizeO;
//Goooooooood the interface to Attribute is so fucked up.
if(!getAttr().checkActive(attrKey)) return;
//TODO wanna remove the binding stuff I think
auto attr = getAttr().getInt(attrKey);
if(attr && getAttr().isConst(attrKey))
{
const int requiredSpace = static_cast<int>(*attr);
prop.proposedDimensions.get(theAngle) = requiredSpace;
prop.expandForAngle(theAngle) = false;
prop.shrinkForAngle(theAngle) = false;
}
else if(attr) //non-const case
{
prop.proposedDimensions.get(theAngle) = int(*attr);
prop.proposedDimensions = prop.roundConflicts(prop.proposedDimensions);
}
};
internal(Angle::Horizontal);
internal(Angle::Vertical);
return prop;
}
std::optional<int> DisplayItem::getAlignmentProperty(const Angle theAngle)
{
return getAttr().getProperty(getRelativeAdjacent() == theAngle
? AttributeKey::AlignAdjacent
: AttributeKey::AlignOpposite);
}
FillPrecedence DisplayItem::returnGreedyIFEVENONECHILDBLINKS(const Angle theAngle)
{
const auto& children = getChildren();
auto pred = [theAngle](DisplayItem* child)
{
return child->computerFillPrecedenceForAngle(theAngle) == FillPrecedence::Greedy;
};
return std::any_of(children.begin(), children.end(), pred) ? FillPrecedence::Greedy
: FillPrecedence::Shrinky;
/// ;; IT'S STILL JUST NOT THE SAAAAAAAAME
///
/// (if (some (lambda (x) (eql (elt something-something-something x) fill-precedence-greedy)) children)
/// 'fill-precedence-greedy
/// 'fill-precedence-shrinky)
///
/// ;; I wanne use lithp :<
}
std::optional<std::string> DisplayItem::getCellName()
{
if(getAttr().checkActive(AttributeKey::PositionWithin))
return std::optional<std::string>();
return getAttr().getSingleValueKey(AttributeKey::PositionWithin)->key;
}
int DisplayItem::getJustificationOffset(const int itemSize, const int cellSize)
{
const auto optional = Default::Filter::getJustificationProperty(this);
if(!optional) return 0;
switch(*optional)
{
case Property::Inner: return 0;
case Property::Center: return (cellSize ? (cellSize / 2) : 0) - (itemSize ? itemSize / 2 : 0);
case Property::Outer: return cellSize - itemSize;
default: assert(false);
}
}
int DisplayItem::getCellAlignmentOffset(const Angle theAngle,
const int cellSize,
const int viewportSize,
const int reservedInner,
const int reservedOuter)
{
const auto optionalProperty = getAlignmentProperty(theAngle);
const int theAlignmentProperty = optionalProperty && (cellSize < viewportSize) ? *optionalProperty
: Default::alignmentProperty;
switch (theAlignmentProperty)
{
case Property::Inner: return reservedInner;
case Property::Center: return (viewportSize ? (viewportSize / 2) : 0) - (cellSize ? cellSize / 2 : 0);
case Property::Outer: return viewportSize - cellSize - reservedOuter;
default: assert(false);
}
}
Coords DisplayItem::getTranslationOffset()
{
Coords trans;
auto optionalTransA = myAttrs.getInt(AttributeKey::TranslationA);
auto optionalTransO = myAttrs.getInt(AttributeKey::TranslationO);
if(optionalTransA) trans.get(getRelativeAdjacent()) = *optionalTransA;
if(optionalTransO) trans.get(getRelativeOpposite()) = *optionalTransO;
return trans;
}
int DisplayItem::getSizeForAngle(const Angle theAngle)
{
auto optionalSize = [&]()
{
if(correctAngle(theAngle) == Angle::Adjacent)
return myAttrs.getInt(AttributeKey::SizeA);
else
return myAttrs.getInt(AttributeKey::SizeO);
}();
return optionalSize ? *optionalSize
: 0;
}
std::optional<FillPrecedence> DisplayItem::filterFillPrecedenceForAngle(const Angle theAngle)
{
const auto attrKey = correctAngle(theAngle) == Angle::Adjacent ? AttributeKey::OverrideFillPrecedenceAdjacent
: AttributeKey::OverrideFillPrecedenceOpposite;
const auto optionalAttr = myAttrs.getProperty(attrKey);
if(optionalAttr)
{
switch(*optionalAttr)
{
case Property::Shrinky: return FillPrecedence::Shrinky;
case Property::Greedy: return FillPrecedence::Greedy;
default: std::terminate(); //Prolly a little drastic... TODO
}
}
return std::optional<FillPrecedence>();
}
FillPrecedence DisplayItem::computerFillPrecedenceForAngle(const Angle theAngle)
{
const auto optionalOverride = filterFillPrecedenceForAngle(theAngle);
if(optionalOverride) return *optionalOverride;
if(myWidget.checkIsSet())
return myWidget.getFillPrecedence(theAngle);
return FillPrecedence::Shrinky; //DEFAULT
}
void DisplayItem::shape(const GeometryProposal officialProposal)
{
GeometryProposal revisedProposal = reviseProposalAsRequired(officialProposal);
revisedProposal.proposedDimensions -= getReservedDimens();
if(revisedProposal.proposedDimensions.w < 0)
revisedProposal.proposedDimensions.w = 0;
if(revisedProposal.proposedDimensions.h < 0)
revisedProposal.proposedDimensions.h = 0;
const Dimens proposedCellSpace = shapeDrawingArea(revisedProposal) + getReservedDimens();
myCellDimens = proposedCellSpace;
myViewportDimens = officialProposal.roundConflicts(proposedCellSpace);
compliantGeometry = officialProposal.verifyCompliance(myCellDimens);
//Cell alignment
relativeCellOffset = getCellAlignmentOffsetMindingReserved(myCellDimens, myViewportDimens);
}
Dimens DisplayItem::shapeDrawingArea(const GeometryProposal officalProposal)
{
if(myWidget.checkIsSet())
{
myWidget.shape(officalProposal);
return myWidget.getSpace();
}
else return officalProposal.proposedDimensions;
}
void DisplayItem::computerAbsoluteOffsets(const Coords parentViewportOffset)
{
absoluteViewportOffset = parentViewportOffset + relativeViewportOffset;
absoluteCellOffset = absoluteViewportOffset + relativeCellOffset;
for(DisplayItem* child : children)
child->computerAbsoluteOffsets(absoluteViewportOffset);
}
void DisplayItem::render()
{
Canvas* theCanvas = myEnv->getCanvas();
const Rect viewportClip(absoluteViewportOffset, myViewportDimens);
//Cell offset already takes margins into account
const Rect cellClip(absoluteCellOffset, myCellDimens);
const Rect borderLessCell = [&]() -> Rect
{
Rect clip = cellClip;
clip.d -= getReservedBorderDimens();
clip.c += getReservedInnerBorderDimens();
return clip;
}();
const Rect contentClip = [&]() -> Rect
{
Rect clip = borderLessCell;
clip.d -= getReservedPaddingDimens();
clip.c += getReservedInnerPaddingDimens();
return clip;
}();
const auto savedAlpha = theCanvas->getAlpha();
theCanvas->setAlpha(savedAlpha * Default::Filter::getAlpha(this));
theCanvas->pushClip(viewportClip);
theCanvas->pushClip(cellClip); //they could overlap
//Draw boxeee
{
//--->Draw borders<---
theCanvas->pushClip(borderLessCell);
const auto elementColor = Default::Filter::getElementColor(this);
if(elementColor)
theCanvas->fillRect(borderLessCell, *elementColor);
theCanvas->popClip(); //borderlessCell
}
if(myWidget.checkIsSet() && myWidget.isDrawable())
{
theCanvas->pushClip(contentClip);
theCanvas->setOffset(contentClip.c);
myWidget.draw(myWidget.isLayout() ? nullptr : theCanvas);
theCanvas->resetOffset();
theCanvas->popClip(); //contentClip
}
theCanvas->popClip();//cellClip
theCanvas->popClip();//viewportClip
theCanvas->setAlpha(savedAlpha); //restored alpha :)
}
void DisplayItem::updateHover()
{
const Coords point = myEnv->getMouseOffsetRelativeToWindow();
if(Rect(absoluteCellOffset, myCellDimens).checkCollision(point))
{
if(myWidget.checkIsSet())
{
myWidget.bubble();
//Should this have direct integration or just let
// the widget do state stuff? I think the latter...
//So... Why does it have a return value TODO
const Coords relativePoint = point - absoluteCellOffset + getReservedInnerPaddingDimens();
myWidget.detectCollisionPoint(relativePoint); //Rename to "handle"?
}
//else without a layout/widget we just assume there's no rules for child collision
// because layouts define all that stuff
//Now we handle our own
if(!theStateManager->checkAny(StateKey(States::Item::HoverExclusive))) //horribly inefficient
theStateManager->mutateIfObserved(StateKey(States::Item::HoverExclusive, this), true);
theStateManager->mutateIfObserved(StateKey(States::Item::HoverInclusive, this), true);
}
}
std::vector<IVD_Element*> DisplayItem::getChildWidgetInStampOrder()
{
std::vector<DisplayItem*> sorted;
for(DisplayItem* child : children)
sorted.push_back(child);
auto compareStamp = [&](DisplayItem* left, DisplayItem* right)
{
return left->getElementStamp() < right->getElementStamp();
};
std::sort(sorted.begin(), sorted.end(), compareStamp);
//---------------------->stable SORT BY NAMED CELLS<------
//Just reverse iterate the named cells list, and then sub-iteratorate
// the result vector, and move the one to the beginning.
//YES I KNOW IT'S SUPER INNEFICIENT. But it might be better
// than a clever solution because named cells will probably never be
// more than like 10, and the children should typically be == to named cells
// so 10*10 isn't a huge deal
//It's probably not even worth testing unless this shows up in during
// profiling.
if(myAttrs.checkActive(AttributeKey::CellNames))
{
auto cells = myAttrs.getLiteralList(AttributeKey::CellNames);
for(std::vector<std::string>::reverse_iterator rit = cells.rbegin(); rit != cells.rend(); ++rit)
{
const std::string& cellName = *rit;
for(std::vector<DisplayItem*>::iterator it = sorted.begin(); it != sorted.end(); ++it)
{
DisplayItem* child = *it;
if(child->getCellName() == cellName)
{
sorted.erase(it);
sorted.insert(sorted.begin(), child);
break;
}
}
}
}
//Then do a special "first"/"last" reorder if you want that feature
// back for whatever reason.
std::vector<IVD_Element*> result;
for(DisplayItem* child : sorted)
result.push_back(reinterpret_cast<IVD_Element*>(child));
return result;
}
IVD_Element* DisplayItem::getChildElementForNamedCell(const std::string name)
{
for(DisplayItem* child : children)
{
auto optionalCellName = getCellName();
if(!optionalCellName) continue;
if(*optionalCellName == name)
return reinterpret_cast<IVD_Element*>(child);
}
return nullptr;
}
}//IVD

309
src/displayitem.h Executable file
View File

@ -0,0 +1,309 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include <set>
#include <map>
#include "attributepositionpair.h"
#include "runtimeattributeset.h"
#include "widget.h"
#include <reprodyne.h>
namespace IVD
{
class Environment;
class ModelContainer;
class StateManager;
class Canvas;
class DisplayItem
{
const int elementStamp;
Element* myElem;
Environment* myEnv;
StateManager* theStateManager;
DisplayItem* parent;
const ValueKeyPath elementPath;
const ReferenceAttributeSet defaultState;
std::map<int, const ReferenceAttributeSet*> contributingAttrs;
RuntimeAttributeSet myAttrs;
//Cell is the actual box we want, adjusted for margins?
//Viewport is the space we have to position in.
bool compliantGeometry = false;
Dimens myCellDimens;
Dimens myViewportDimens; //We need the viewport for the clip
Coords relativeViewportOffset; //Relative to parent
Coords relativeCellOffset; //Relative to viewport
//These are very invalid during shaping
Coords absoluteViewportOffset;
Coords absoluteCellOffset;
WidgetWrapper myWidget;
Canvas* theCanvas = nullptr;
std::set<DisplayItem*> children;
std::map<ValueKey, double> variableMap;
Angle getRelativeAdjacent();
Angle getRelativeOpposite()
{ return getRelativeAdjacent() == Angle::Horizontal ? Angle::Vertical
: Angle::Horizontal; }
Angle correctAngle(const Angle theAngle);
int getReservedForKey(Angle theAngle, const int adjacentKey, const int oppositeKey);
std::optional<int> getAlignmentProperty(const Angle theAngle);
//putting this here cause I wanna save it but don't know where to put
// it yet. Probably gonna be placed beyond the C boundary as a helper
// eventually.
FillPrecedence returnGreedyIFEVENONECHILDBLINKS(const Angle theAngle);
std::optional<std::string> getCellName();
public:
DisplayItem(Element* elem,
Environment* theEnv,
StateManager* theStateManager,
const ReferenceAttributeSet& theDefaultState,
const ValueKeyPath &elemPath,
const int elementStamp):
myElem(elem),
myEnv(theEnv),
parent(nullptr),
elementPath(elemPath),
elementStamp(elementStamp),
defaultState(theDefaultState),
theStateManager(theStateManager),
myAttrs(this, theDefaultState)
{
reprodyne_open_scope(this);
}
Element* getElement()
{ return myElem; }
void destroyWidget()
{ myWidget.destroy(); }
IVD_Widget* setupNewWidget(const WidgetBlueprints blueprints)
{
myWidget.reset(myEnv, blueprints);
return myWidget.get();
}
IVD_Widget* getWidget()
{ return myWidget.get(); }
void reactToTrigger(const std::string trigg)
{ myWidget.handleTrigger(trigg); }
int getElementStamp() { return elementStamp; }
//TODO: warn if the variable isn't found. Don't take that shit from noone.
void setVariable(const ValueKey key, const double val)
{ variableMap[key] = val; }
double getVariable(const ValueKey key)
{ return variableMap[key]; }
Environment* getEnv()
{ return myEnv; }
void addAttributeSet(const AttributePositionPair pair);
void removeAttributeSet(const AttributePositionPair pair);
void recomputeAttributeSet();
RuntimeAttributeSet& getAttr()
{ return myAttrs; }
void setParent(DisplayItem* item);
void deparent();
DisplayItem* getParent()
{ return parent; }
void prepareToDie();
bool hasParent()
{ return parent; }
DisplayItem* getRoot();
ValueKeyPath getElementPath()
{ return elementPath; }
int getDepth();
//TODO: Borderssssssssssssssssssss
int getInnerMargin(Angle theAngle)
{ return getReservedForKey(theAngle, AttributeKey::MarginAdjIn, AttributeKey::MarginOppIn); }
int getOuterMargin(Angle theAngle)
{ return getReservedForKey(theAngle, AttributeKey::MarginAdjOut, AttributeKey::MarginOppOut); }
int getInnerPadding(Angle theAngle)
{ return getReservedForKey(theAngle, AttributeKey::PaddingAdjIn, AttributeKey::PaddingOppIn); }
int getOuterPadding(Angle theAngle)
{ return getReservedForKey(theAngle, AttributeKey::PaddingAdjOut, AttributeKey::PaddingOppOut); }
int getReservedMargin(Angle theAngle)
{ return getInnerMargin(theAngle) + getOuterMargin(theAngle); }
int getReservedPadding(Angle theAngle)
{ return getInnerPadding(theAngle) + getOuterPadding(theAngle); }
int getReservedInner(Angle theAngle)
{ return getInnerMargin(theAngle) + getInnerPadding(theAngle); }
int getReservedOuter(Angle theAngle)
{ return getOuterMargin(theAngle) + getOuterPadding(theAngle); }
int getReserved(Angle theAngle)
{ return getReservedInner(theAngle) + getReservedOuter(theAngle); }
Dimens getReservedInnerMarginDimens()
{ return Dimens(getInnerMargin(getRelativeAdjacent()),
getInnerMargin(getRelativeOpposite())); }
Dimens getReservedInnerPaddingDimens()
{ return Dimens(getInnerPadding(getRelativeAdjacent()),
getInnerPadding(getRelativeOpposite())); }
Dimens getReservedOuterMarginDimens()
{ return Dimens(getOuterMargin(getRelativeAdjacent()),
getOuterMargin(getRelativeOpposite())); }
Dimens getReservedOuterPaddingDimens()
{ return Dimens(getOuterPadding(getRelativeAdjacent()),
getOuterPadding(getRelativeOpposite())); }
Dimens getReservedPaddingDimens()
{ return getReservedInnerPaddingDimens() + getReservedOuterPaddingDimens(); }
Dimens getReservedMarginDimens()
{ return getReservedInnerMarginDimens() + getReservedOuterMarginDimens(); }
Dimens getReservedInnerDimens()
{ return Dimens(getReservedInner(getRelativeAdjacent()),
getReservedInner(getRelativeOpposite())); }
Dimens getReservedOuterDimens()
{ return Dimens(getReservedOuter(getRelativeAdjacent()),
getReservedOuter(getRelativeOpposite())); }
Dimens getReservedDimens()
{ return getReservedInnerDimens() + getReservedOuterDimens(); }
Dimens getReservedInnerBorderDimens()
{ return Dimens(); }
Dimens getReservedOuterBorderDimens()
{ return Dimens(); }
Dimens getReservedBorderDimens()
{ return getReservedInnerBorderDimens() + getReservedOuterBorderDimens(); }
GeometryProposal reviseProposalAsRequired(GeometryProposal prop);
bool checkCellAlignmentOffset(const Angle theAngle)
{ return (bool)getAlignmentProperty(theAngle); }
int getJustificationOffset(const int itemSize, const int cellSize);
int getCellAlignmentOffset(const Angle theAngle,
const int cellSize,
const int viewportSize,
const int reservedInner,
const int reservedOuter);
int getCellAlignmentOffset(const Angle theAngle, const int cellSize, const int viewportSize)
{
return getCellAlignmentOffset(theAngle,
cellSize,
viewportSize,
getReservedInner(theAngle),
getReservedOuter(theAngle));
}
Coords getCellAlignmentOffset(const Dimens cellSize,
const Dimens viewportSize,
const Dimens reservedInner,
const Dimens reservedOuter)
{
auto get = [&](const Angle theAngle)
{
return getCellAlignmentOffset(theAngle,
cellSize.get(theAngle),
viewportSize.get(theAngle),
reservedInner.get(theAngle),
reservedOuter.get(theAngle));
};
return Coords(get(Angle::Horizontal), get(Angle::Vertical));
}
Coords getCellAlignmentOffsetMindingReserved(const Dimens cellSize,
const Dimens viewportSize)
{
return getCellAlignmentOffset(cellSize,
viewportSize,
getReservedInnerDimens(),
getReservedOuterDimens());
}
Coords getTranslationOffset();
int getSizeForAngle(const Angle theAngle);
std::optional<FillPrecedence> filterFillPrecedenceForAngle(const Angle theAngle);
void setOffset(const Coords offset)
{ relativeViewportOffset = offset; }
Dimens getViewportDimens() const
{ return myViewportDimens; }
FillPrecedence computerFillPrecedenceForAngle(const Angle theAngle);
void shape(const GeometryProposal officialProposal);
Dimens shapeDrawingArea(const GeometryProposal officalProposal);
//This figures the absolute offset for the viewport, not the cell!
//The relative offset for the viewport, is the offset relative to the parent
// layout.
void computerAbsoluteOffsets(const Coords parentViewportOffset);
//Canvas should always be set before call to render
void render();
void updateHover();
//I wish the reference for the container could be const,
// without that propogating to the pointers...
const std::set<DisplayItem*>& getChildren()
{ return children; }
std::vector<IVD_Element*> getChildWidgetInStampOrder();
IVD_Element* getChildElementForNamedCell(const std::string name);
const int childCount()
{ return children.size(); }
};
}//IVD

53
src/driver.h Normal file
View File

@ -0,0 +1,53 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef DRIVER_H
#define DRIVER_H
#include "geometry.h"
namespace IVD
{
class DisplayItem;
class StateManager;
class Canvas;
class Driver
{
StateManager* myStateManager;
protected:
StateManager* getStateManager()
{ return myStateManager; }
public:
Driver(): myStateManager(nullptr) {}
virtual ~Driver() {}
void setStateManager(StateManager* theStateManager)
{ myStateManager = theStateManager; }
virtual void addDisplayItem(DisplayItem* item) = 0;
virtual void removeDisplayItem(DisplayItem* item) = 0;
virtual void processEvents() = 0;
virtual bool checkHoverInvalidated() = 0;
virtual DisplayItem* getWindowItemWithMouseFocus() = 0;
virtual Coords getMousePointRelativeToWindow() = 0;
virtual void invalidateGeometry(DisplayItem* item) = 0;
virtual void invalidatePosition(DisplayItem* item) = 0;
virtual void invalidateCanvas(DisplayItem* item) = 0;
virtual void invalidateTitleText(DisplayItem* item) = 0;
virtual void invalidateVisibility(DisplayItem* item) = 0;
virtual Canvas* getCanvas() = 0;
virtual void refresh() = 0;
virtual bool checkAnythingToDo() = 0;
};
}
#endif // DRIVER_H

139
src/element.h Executable file
View File

@ -0,0 +1,139 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef ELEMENT_H
#define ELEMENT_H
#include <set>
#include <map>
#include "assert.h"
#include "statekey.h"
#include "referenceattributeset.h"
#include "attributepositionpair.h"
#include "virtualstatekey.h"
namespace IVD
{
class DisplayItem;
//Element probably isn't the best term for this, because
// they're still essentially templates for instances of
// DisplayItems.
class Element
{
int elementStamp = -42;
ValueKeyPath path;
ValueKeyPath modelPath;
ReferenceAttributeSet defaultAttr;
std::vector<ReferenceAttributeSet> keyedAttributes;
std::map<ScopedValueKey, int> keyedAttributeMap;
std::vector<VirtualStateKeyPrecursor> virtualKeys;
std::map<ValueKey, Expression> variableInitialExpressions;
//I have no idea what this is doing anymore
int obtainPosForKey(const ScopedValueKey key)
{
auto it = keyedAttributeMap.find(key);
if(it == keyedAttributeMap.end())
{
const int pos = keyedAttributes.size();
keyedAttributes.emplace_back(defaultAttr.size());
keyedAttributeMap[key] = pos;
return pos;
}
return it->second;
}
public:
Element(const int stamp, const int attributeCount):
elementStamp(stamp),
defaultAttr(attributeCount)
{}
int getElementStamp() { return elementStamp; }
const ValueKeyPath& getPath() const
{ return path; }
void setPath(const ValueKeyPath& theName)
{ path = theName; }
void setModelPath(ValueKeyPath key)
{ modelPath = key; }
void applyToEachScopedValueKey(std::function<void(ScopedValueKey&)> fun)
{
defaultAttr.applyToEachScopedValueKey(fun);
for(auto& attr : keyedAttributes) attr.applyToEachScopedValueKey(fun);
}
void addVirtualStateKey(VirtualStateKeyPrecursor vskey) { virtualKeys.push_back(vskey); }
void setInitialExpression(const ValueKey key, Expression initial)
{ variableInitialExpressions[key] = initial; }
auto getVariableInitialExpressions()
{ return variableInitialExpressions; }
std::vector<VirtualStateKeyPrecursor> getVirtualKeys() { return virtualKeys; }
ReferenceAttributeSet& getDefaultAttr() { return defaultAttr; }
std::map<ScopedValueKey, int> getKeyedAttributeMap() { return keyedAttributeMap; }
ValueKeyPath getModelPath() const { return modelPath; }
ReferenceAttributeSet& getAttributeSetForStateKey(ScopedValueKey statePre)
{
if(!statePre.key) return defaultAttr;
const int pos = obtainPosForKey(statePre);
return keyedAttributes.at(pos);
}
AttributePositionPair at(const int pos)
{ return AttributePositionPair(pos, &keyedAttributes.at(pos)); }
void deriveFrom(Element& parentClass)
{
defaultAttr.deriveFrom(parentClass.defaultAttr);
for(const auto& pair : parentClass.keyedAttributeMap)
{
const auto key = pair.first;
const int myPos = obtainPosForKey(key);
const int otherPos = parentClass.obtainPosForKey(key);
keyedAttributes.at(myPos).deriveFrom(parentClass.keyedAttributes.at(otherPos));
}
//ALRIGHT SO
//This is broken TODO
//I don't remember when I broke it, but it's been broken since I broke IVD off
// from the original repo.
//I know it used to work because there is still a validation test that some ancient
// build produced correctly.
//I spent a while trying to track this down, thinking I recently broke something and
// there was some sort of missing ampersand or something, but nope. Been like this since
// the initial commit.
//I'm not going to do anything about it right now because obviously things were (mostly)
// working before I started the refactor I'm at the tail-end of now, and I just wanna get
// back to that state first. I'm leaving this comment so that I don't waste time in the future.
//Basically, the below line doesn't work because we want to merge states that have equivalent
// virtual keys. This is non-trivial, obviously.
//I vaguely remember deleting the code thinking it was kludgy? I don't remember why. Or if I didn't
// realize what I was doing. But yeaaaah it doesn't work.
//for(auto& vk : parentClass.virtualKeys) virtualKeys.push_back(vk);
}
};
}//IVD
#endif // ELEMENT_H

760
src/environment.cpp Executable file
View File

@ -0,0 +1,760 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "environment.h"
#include "displayitem.h"
#include "states.h"
#include "defaults.h"
#include "driver.h"
#include <reprodyne.h>
#include "user_include/IVD_status.h"
#include <iostream>
#include <chrono>
#include <thread>
namespace IVD
{
Driver* createDefaultDriver();
Environment::Environment():
managedDriver(createDefaultDriver()),
myDriver(managedDriver.get())
{
initOthers();
}
void Environment::initOthers()
{
managedDriver->setStateManager(&myStateManager);
}
StateKey Environment::generateStateKeyFromPrecursor(ScopedValueKey precursor, DisplayItem* baseContext)
{
assert(precursor.key);
StateKey myStateKey;
myStateKey.identity = *precursor.key;
if(precursor.myScope == ScopedValueKey::Scope::Element)
myStateKey.scope = baseContext;
else if(precursor.myScope == ScopedValueKey::Scope::Model)
{
assert(false);
}
//Global is default, scope is nullptr already.
if(precursor.path)
{
auto optionalTarget = deduceTarget(baseContext, *precursor.path);
assert(optionalTarget);
myStateKey.scope = *optionalTarget;
}
return myStateKey;
}
void Environment::processDeferredVirtualStates()
{
for(auto vskeyp : deferredVirtualStateKeys)
{
VirtualStateKey vskey = vskeyp.generateVirtualStateKey(this);
myStateManager.insertVirtualState(vskey);
}
deferredVirtualStateKeys.clear();
}
void Environment::processDeferredPositioning()
{
for(auto pair : deferredPositioning)
positionDisplayItemInDrawTree(pair.item, pair.parent);
processDeferredVirtualStates();
}
DisplayItem* Environment::setupNewDisplayItem(Element* elem)
{
DisplayItem* item = nullptr;
{
std::unique_ptr<DisplayItem> itemUniquePtr =
std::make_unique<DisplayItem>(elem,
this,
&myStateManager,
elem->getDefaultAttr(),
elem->getPath(),
elem->getElementStamp());
item = itemUniquePtr.get();
instances[item] = std::move(itemUniquePtr);
}
elementToDisplayItems[elem].insert(item);
for(auto val : elem->getKeyedAttributeMap())
{
const auto preKey = val.first;
const auto pos = val.second;
const auto pair = elem->at(pos);
myStateManager.registerStateObserver(generateStateKeyFromPrecursor(preKey, item), item, pair);
}
for(VirtualStateKeyPrecursor vskeyp : elem->getVirtualKeys())
{
vskeyp.context = item;
deferredVirtualStateKeys.push_back(vskeyp);
//These are deferred, because they might reference other states that haven't
// been registered yet, and they'll miss the hook otherwise. Items are instantiated
// in batches in lots of cases, the batches having the necessary states.
//We can't even create the virtualstatekey, because it may reference states
// that we haven't *inserted* yet.
//Is this still true after the model rewrite? ~~ feb 2021
}
for(auto pair : elem->getVariableInitialExpressions())
item->setVariable(pair.first, pair.second.solve(item));
itemsWithChangedAttributeSets.insert(item);
return item;
}
void Environment::destroyDisplayItem(DisplayItem* item)
{
DisplayItem*& youKillMyFather = item;
//Hello, my name is Inigo Montoya,
youKillMyFather->prepareToDie();
assert(!triggerMap.count(item));
//A word on widgets.
//Any widget item that references this item is about to be destroyed anyway,
// if there is a widget, it's removal is what triggered this. So don't worry
// about it's references.
itemsWithChangedAttributeSets.erase(item);
myStateManager.removeReferencesToDisplayItem(item);
elementToDisplayItems.at(item->getElement()).erase(item);
instances.erase(item); //Don't you just love unique_ptr?
}
void Environment::positionDisplayItemInDrawTree(DisplayItem* item, IVD_Widget* parentWidget = nullptr)
{
if(!parentWidget && widgetToDisplayItem.count(item->getWidget()))
return; //permanent custody
const auto posPath = Default::Filter::getPositionWithin(item);
if(posPath && posPath->size() && posPath->at(0) == "Environment")
{
if(item->getParent())
{
myDriver->invalidateGeometry(item->getParent());
item->deparent();
}
myDriver->addDisplayItem(item);
myDriver->invalidateGeometry(item);
return;
}
myDriver->removeDisplayItem(item);
if(parentWidget)
{
DisplayItem* parentItem = widgetToDisplayItem.at(parentWidget);
item->setParent(parentItem);
markAsBadGeometry(parentItem);
}
else if(posPath)
{
ValueKeyPath path = *posPath;
auto target = deduceTarget(item, path);
if(!target)
{
std::cerr << "IVD Runtime: Could not position [" << item->getElementPath() << "] ";
if(widgetToDisplayItem.count(item->getWidget()))
std::cerr << "-> [Widget] ";
std::cerr << "within [" << path << "] "
<< "-> [Widget/Static], could not deduce parent."
<< std::endl;
return;
}
if(item->getParent())
myDriver->invalidateGeometry(item->getParent());
item->setParent(*target);
myDriver->invalidateGeometry(*target);
}
else
{
myDriver->invalidateGeometry(item->getParent());
item->deparent();
}
}
void Environment::setWidget(DisplayItem *item)
{
if(widgetToDisplayItem.count(item->getWidget()))
return; //it be bound by BLOOD
item->destroyWidget();
WidgetBlueprints blueprints;
if(auto optionalLayoutName = item->getAttr().getUserToken(AttributeKey::Layout))
{
blueprints = layoutBlueprints.at(*optionalLayoutName);
}
else if(auto optionalWidgetName = item->getAttr().getUserToken(AttributeKey::Widget))
{
blueprints = widgetBlueprints.at(*optionalWidgetName);
}
else return; //Otherwise, we leave it blank, because it's not actually needed ^^
widgetToDisplayItem[item->setupNewWidget(blueprints)] = item;
}
std::optional<DisplayItem*> Environment::deduceTarget(DisplayItem *context, const ValueKeyPath key)
{
//This used to be a lot more sophisticated which is why it's kinda weird looking now
// but leaving it here because I'm still unsure of the future direction.
std::optional<DisplayItem*> result;
Element* targetElem = elementLookupByPath[key];
const auto count = elementToDisplayItems.at(targetElem).size();
if(count > 1)
std::cout << "IVD Runtime Warning: Target Ambiguous: " << key << std::endl;
//Grab the first
for(DisplayItem* target : elementToDisplayItems.at(targetElem))
return target;
return result;
}
double Environment::commonExternalAccessor(DisplayItem* context,
const ScopedValueKey key,
std::optional<double> value)
{
if(key.myScope == ScopedValueKey::Scope::Model)
{
std::cerr << "Warning dead code path XXX" << std::endl;
return 42;
}
//And NOW it can be a material function... But only on context
assert(key.myScope == ScopedValueKey::Scope::Element);
assert(key.key);
DisplayItem* other = nullptr;
if(!key.path) other = context; //Self referential.
else
{
auto result = deduceTarget(context, *key.path);
assert(result);
other = *result;
}
if(auto optional = myComp.getSymbolForLiteral(*key.key))
{
if(value) other->getAttr().setDeclaredInt(*key.key, *value);
else return *context->getAttr().getDeclaredInt(*key.key);
}
else //User variable
{
if(value) context->setVariable(*key.key, *value);
else return context->getVariable(*key.key);
}
//If we change anything... Yeah.
markAsBadGeometry(context);
return 0;
}
void Environment::markAsBadGeometry(DisplayItem *item)
{
myDriver->invalidateGeometry(item);
}
void Environment::markAsBadCanvas(DisplayItem *item)
{
myDriver->invalidateCanvas(item);
}
void Environment::updateHover()
{
myStateManager.mutateAll(States::Item::HoverExclusive, false);
myStateManager.mutateAll(States::Item::HoverInclusive, false);
DisplayItem* root = myDriver->getWindowItemWithMouseFocus();
if(!root) return;
root->updateHover();
}
Coords Environment::getMouseOffsetRelativeToWindow()
{ return myDriver->getMousePointRelativeToWindow(); }
void Environment::run()
{
while(true)
{
reprodyne_mark_frame();
processDeferredPositioning();
const uint64_t lastStateStampBeforeLoop = myStateManager.getLastStamp();
{
auto mySet = itemsWithChangedAttributeSets;
itemsWithChangedAttributeSets.clear();
for(DisplayItem* item : mySet)
{
item->recomputeAttributeSet();
item->getAttr().executeStateChangers();
item->getAttr().fireSets();
}
}
for(AnimatableAttribute* a : attributeWantsAnimationTick)
{
//There's a bug in GCC's std::set that I haven't been able to reliably reproduce to report.
// (Aggressive compiler optimizations probably makes isolation difficult...)
//But without this conditional, it crashes -somtimes-.
if(!attributeWantsAnimationTick.size()) break;
a->animationTick();
}
//HAS to come after recomputeAttributeSet, which accepts changes
myStateManager.resetTriggerStates();
//------------------------------------------------------------Draw tree frozen beyond this point
//These take precedence because the others will reference windows that otherwise
// don't exist...
{
auto myDrawTreeMutatingAttributes = attributesThatMutateTheDrawTree;
attributesThatMutateTheDrawTree.clear();
for(AnimatableAttribute* attr : myDrawTreeMutatingAttributes)
{
attr->executeChangeAcceptor();
}
//Do the do
auto myAttributesChanged = attributesThatHaveChanged;
attributesThatHaveChanged.clear();
for(AnimatableAttribute* attr : myAttributesChanged)
{
attr->executeChangeAcceptor();
}
}
{
bool die = false;
for(auto it : triggerMap)
{
DisplayItem* item = it.first;
for(auto triggerKey : it.second) //Usually only one, but w/e
{
if(triggerKey.myScope == ScopedValueKey::Scope::Model)
item->reactToTrigger(*triggerKey.key);
//Elemental scope means nothing... But it's the default if nothing else is specified.
//Soo... We just assume anything that's not model is global. It's hacky, but eh.
//It works for now without reworking part of the compiler...
else
{
if(triggerKey.key == Default::quitTrigger)
die = true; //Guarantee that all triggers get called. Safe shutdown, etc...
//else trigger unrecognized
}
}
}
triggerMap.clear();//Mother FUCKER
if(die) break;
}
myDriver->processEvents();
if(myDriver->checkHoverInvalidated()) updateHover();
myDriver->refresh(); //<--- This is expected to handle reprodyne video frame validation
if(!attributeWantsAnimationTick.size() &&
!myDriver->checkAnythingToDo() &&
lastStateStampBeforeLoop == myStateManager.getLastStamp())
{
//Sleepy pea.
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
}
int Environment::loadFromIVDFile(const char* path)
{
if(!myComp.compileFile(path)) return IVD_STATUS_FILE_NOT_FOUND;
if(myComp.getErrorMessages().size()) return IVD_STATUS_COMPILE_ERROR;
for(Element& elem : myComp.getElements())
{
elementLookupByPath[elem.getPath()] = &elem;
if(elem.getModelPath().size())
{
//DIRTY HAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//FIX THE COMPILER
elementModelLookup[elem.getModelPath()[0]] = &elem;
//Defer actual instantiation until the main loop, as it's a continuous process.
continue;
}
//Spin-up static elements
setupNewDisplayItem(&elem);
}
processDeferredVirtualStates();
return IVD_STATUS_SUCCESS;
}
IVD_Widget* Environment::createWidget(const std::string name, IVD_Widget* parent)
{
DisplayItem* item = setupNewDisplayItem(elementModelLookup[name]);
IVD_Widget* widget = item->setupNewWidget(widgetBlueprints.at(name));
widgetToDisplayItem[widget] = item;
//We have to defer parenting because otherwise
// the children potentially created in the constructor
// of widget will be trying to position in a parent not
// yet registered in userOwnedWidgets.
deferredPositioning.push_back({item, parent});
}
IVD_Element* Environment::createIVDelementFromClass(const std::string className, IVD_Widget* parent)
{
Element* classElement = myComp.getElementForClass(className);
if(!classElement || !parent)
{
//todo runtime error
return nullptr;
}
DisplayItem* item = setupNewDisplayItem(classElement);
deferredPositioning.push_back({item, parent});
return reinterpret_cast<IVD_Element*>(item);
}
void Environment::destroyWidget(IVD_Widget* widget)
{
DisplayItem* item = widgetToDisplayItem.at(widget);
widgetToDisplayItem.erase(widget);
if(widgetOwnedDisplayItems.count(widget))
{
for(DisplayItem* item : widgetOwnedDisplayItems.at(widget))
destroyDisplayItem(item);
}
markAsBadGeometry(item); //okayyyy is this safe AT ALL?? With models there was a comment about root windows XXX
destroyDisplayItem(item); //Calls the dtor in the blueprints.
//Unlike construction, child destruction should be straight forward.
}
void Environment::destroyIVDelement(IVD_Widget* parent, IVD_Element* elem)
{
if(!parent)
{
//todo
std::terminate(); //HOLD IT RIGHT THERE
}
DisplayItem* item = reinterpret_cast<DisplayItem*>(elem);
widgetOwnedDisplayItems.at(parent).erase(item);
markAsBadGeometry(item);
destroyDisplayItem(item);
}
Canvas* Environment::getCanvas()
{ return myDriver->getCanvas(); }
double Environment::getInteger(DisplayItem* context, const ScopedValueKey key)
{
if(key.myScope == ScopedValueKey::Scope::Global &&
key.path &&
key.path->size() == 1 &&
key.path->front() == Default::MousePath)
{
assert(key.key);
//Should this be relative to the item or the window?
// by default it's relative to window... TODO
if(key.key == myComp.getLiteralForSymbol(AttributeKey::TranslationO))
return myDriver->getMousePointRelativeToWindow().x;
if(key.key == myComp.getLiteralForSymbol(AttributeKey::TranslationA))
return myDriver->getMousePointRelativeToWindow().y;
//TODO needs scroll distance and there is no keyword for it.
//Not a mouse
}
return commonExternalAccessor(context, key, std::optional<double>());
}
std::string Environment::getString(DisplayItem* context, const ScopedValueKey key)
{
return "Dead code path";
}
void Environment::setupEnvironmentCallbacksOnAttributeForKey(AnimatableAttribute* attr, const int key)
{
//Maybe check if even active?
//Bleh, these should all be stored lambdas, and just send a reference to the attributes.......
using namespace AttributeKey;
attr->setAnimationTickRequester([&](AnimatableAttribute* attr)
{ attributeWantsAnimationTick.insert(attr); });
attr->setCancelAnimationTicker([&](AnimatableAttribute* attr)
{ attributeWantsAnimationTick.erase(attr); });
if(key == PositionWithin)
{
attr->setSignalChangedAttribute([&](AnimatableAttribute* attr)
{ attributesThatMutateTheDrawTree.insert(attr); });
}
else
{
attr->setSignalChangedAttribute([&](AnimatableAttribute* attr)
{ attributesThatHaveChanged.insert(attr); });
}
//Just a little helper for the state changing attributes~
auto applyToStates = [&](AnimatableAttribute* attr, std::function<void(const StateKey key)> fun)
{
auto states = attr->getValueKeyList();
if(!states.size()) return; //TODO Should this... EVER be possible? Assert here fails.
for(const ScopedValueKey pre : states)
{
const StateKey key = generateStateKeyFromPrecursor(pre, attr->revealContext());
fun(key);
}
};
switch(key)
{
case PositionWithin:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
positionDisplayItemInDrawTree(attr->revealContext());
});
break;
case TitleText:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
myDriver->invalidateTitleText(attr->revealContext());
});
break;
case Text:
case ImagePath:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
markAsBadGeometry(attr->revealContext());
});
break;
case Font:
case FontSize:
case TranslationO:
case TranslationA:
case MarginOppOut:
case MarginOppIn:
case MarginAdjIn:
case MarginAdjOut:
case PaddingOppOut:
case PaddingOppIn:
case PaddingAdjIn:
case PaddingAdjOut:
case SizeO:
case SizeA:
case AttributeKey::Orientation:
case WindowSizeStrategy:
case CellNames:
case Justify:
case AlignAdjacent:
case AlignOpposite:
case OverrideFillPrecedenceAdjacent:
case OverrideFillPrecedenceOpposite:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
markAsBadGeometry(attr->revealContext());
});
break;
case Visibility:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
myDriver->invalidateVisibility(attr->revealContext());
});
break;
case Borderless:
case Resizable:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
assert(false);
});
break;
case ElementColor:
case FontColor:
case BorderColor:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
markAsBadCanvas(attr->revealContext());
});
break;
case Layout:
case Widget:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
setWidget(attr->revealContext());
});
break;
case WindowState:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
//assert(false); //I dunno
});
break;
case InduceState:
attr->setChangeAcceptor([&, applyToStates](AnimatableAttribute* attr)
{
applyToStates(attr, [&](const StateKey key)
{
myStateManager.mutateIfObserved(key, true);
});
});
break;
case BindState:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
//silent failure... Feck.
});
break;
case ToggleState:
attr->setChangeAcceptor([&, applyToStates](AnimatableAttribute* attr)
{
applyToStates(attr, [&](const StateKey key)
{
if(myStateManager.checkState(key))
myStateManager.mutateIfObserved(key, false);
else
myStateManager.mutateIfObserved(key, true);
});
});
break;
case UnsetState:
attr->setChangeAcceptor([&, applyToStates](AnimatableAttribute* attr)
{
applyToStates(attr, [&](const StateKey key)
{
myStateManager.mutateIfObserved(key, false);
});
});
break;
case TriggerState:
attr->setChangeAcceptor([&, applyToStates](AnimatableAttribute* attr)
{
applyToStates(attr, [&](const StateKey key)
{
myStateManager.setTriggerIfObserved(key);
});
});
break;
case RadioState:
attr->setChangeAcceptor([&, applyToStates](AnimatableAttribute* attr)
{
StateKey topKey;
uint64_t lastStamp = 0;
int count = 0;
applyToStates(attr, [&](const StateKey key)
{
if(!myStateManager.checkState(key)) return;
const uint64_t myStamp = myStateManager.getStamp(key);
if(myStamp > lastStamp)
{
lastStamp = myStamp;
topKey = key;
}
++count;
});
if(count > 1)
{
applyToStates(attr, [&](const StateKey key)
{
if(key == topKey) return;
myStateManager.mutateIfObserved(key, false);
});
}
});
break;
case Triggers:
attr->setChangeAcceptor([&](AnimatableAttribute* attr)
{
DisplayItem* item = attr->revealContext();
auto triggers = item->getAttr().getValueKeyList(AttributeKey::Triggers);
if(triggers.size()) triggerMap[item] = triggers;
});
break;
}
}
}//IVD

144
src/environment.h Executable file
View File

@ -0,0 +1,144 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H
#include <string>
#include <vector>
#include <list>
#include "compiler.h"
#include "statemanager.h"
#include "displayitem.h"
#include "element.h"
#include "statekey.h"
#include "virtualstatekey.h"
#include "widget.h"
#include "OpenImageIO/imagecache.h"
namespace IVD
{
class AttributeSet;
class Driver;
class Environment
{
std::unique_ptr<Driver> managedDriver;
//Oh
Driver* myDriver;
Compiler myComp;
StateManager myStateManager;
std::vector<VirtualStateKeyPrecursor> deferredVirtualStateKeys;
struct DeferredPositioning
{
DisplayItem* item;
IVD_Widget* parent;
};
std::vector<DeferredPositioning> deferredPositioning;
std::map<DisplayItem*, std::unique_ptr<DisplayItem>> instances;
std::map<IVD_Widget*, DisplayItem*> widgetToDisplayItem;
std::map<IVD_Widget*, std::set<DisplayItem*>> widgetOwnedDisplayItems;
//This is for tracking items that need recomputing.
std::set<DisplayItem*> itemsWithChangedAttributeSets;
//These are for tracking attributes that have been recomputingted.
std::set<AnimatableAttribute*> attributeWantsAnimationTick;
std::set<AnimatableAttribute*> attributesThatHaveChanged; //No really we're changed let us out
std::set<AnimatableAttribute*> attributesThatMutateTheDrawTree; //Not convinced this is necessary...
std::map<DisplayItem*, std::vector<ScopedValueKey>> triggerMap;
std::map<ValueKeyPath, Element*> elementLookupByPath;
std::map<std::string, Element*> elementModelLookup;
std::map<Element*, std::set<DisplayItem*>> elementToDisplayItems;
std::map<std::string, WidgetBlueprints> widgetBlueprints;
std::map<std::string, WidgetBlueprints> layoutBlueprints;
void initOthers();
void processDeferredVirtualStates();
void processDeferredPositioning();
DisplayItem* setupNewDisplayItem(Element* elem);
void destroyDisplayItem(DisplayItem* item);
void positionDisplayItemInDrawTree(DisplayItem* item, IVD_Widget *parentWidget);
void setWidget(DisplayItem* item);
std::optional<DisplayItem *> deduceTarget(DisplayItem* context, const ValueKeyPath key);
double commonExternalAccessor(DisplayItem* context,
const ScopedValueKey key,
std::optional<double> value);
void markAsBadGeometry(DisplayItem* item);
void markAsBadCanvas(DisplayItem* item);
void updateHover();
public:
Environment();
//The big one
void run();
int loadFromIVDFile(const char* path);
void registerWidgetBlueprints(const std::string name, const WidgetBlueprints blueprints)
{ widgetBlueprints[name] = blueprints; }
void registerLayoutBlueprints(const std::string name, const WidgetBlueprints blueprints)
{ layoutBlueprints[name] = blueprints; }
IVD_Widget* createWidget(const std::string name, IVD_Widget* parent);
IVD_Element* createIVDelementFromClass(const std::string className, IVD_Widget* parent);
void destroyWidget(IVD_Widget* widget);
void destroyIVDelement(IVD_Widget* parent, IVD_Element* elem);
//Doesn't work for layoutssssssss....
DisplayItem* getUnderlyingDisplayItemForWidget(IVD_Widget* widget)
{ return widgetToDisplayItem.at(widget); }
Canvas* getCanvas();
Compiler* getCompiler()
{ return &myComp; }
Coords getMouseOffsetRelativeToWindow();
IVD_Widget* getChildWidgetForNamedCell(IVD_Widget* parent, const std::string name);
const char* getCompilerErrors()
{ return myComp.getErrorMessageDigest().c_str(); }
double getInteger(DisplayItem* context, const ScopedValueKey key);
void setInteger(DisplayItem* context, const ScopedValueKey key, const double val)
{ commonExternalAccessor(context, key, val); }
std::string getString(DisplayItem* context, const ScopedValueKey key);
void markAsChangedAttributes(DisplayItem* item)
{ itemsWithChangedAttributeSets.insert(item); }
void setupEnvironmentCallbacksOnAttributeForKey(AnimatableAttribute* attr, const int key);
StateKey generateStateKeyFromPrecursor(ScopedValueKey precursor, DisplayItem* baseContext);
};
}//IVD
#endif // ENVIRONMENT_H

161
src/expression.cpp Normal file
View File

@ -0,0 +1,161 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "expression.h"
#include <iostream>
#include "environment.h"
#include "binaryexpressionprinter.h"
namespace IVD
{
std::string ExpressionNode::printoutThySelf() const
{
std::string literal; //not to be confused with a standard string literal
switch(operation)
{
case Keyword::ScalarType:
literal = std::to_string(val);
literal.push_back('i');
break;
case Keyword::UnitPercent:
literal = std::to_string(val);
literal.push_back('%');
break;
case Keyword::UnitStandard:
literal = std::to_string(val);
literal.push_back('u');
break;
case IVD::Keyword::ScopedValueKey:
if(weakTerm) literal = "[";
literal += extVal.generatePrintout();
if(weakTerm) literal += "]";
break;
case IVD::Keyword::OperatorPlus:
literal = "(+)";
break;
case IVD::Keyword::OperatorMinus:
literal = "(-)";
break;
case IVD::Keyword::OperatorTimes:
literal = "(*)";
break;
case IVD::Keyword::OperatorDivide:
literal = "(/)";
break;
default: assert(false);
}
return literal;
}
double Expression::solveNode(const ExpressionNode* theNode, DisplayItem* theContext) const
{
if(!theNode) return 0;
const double left = solveNode(theNode->left.get(), theContext);
const double right = solveNode(theNode->right.get(), theContext);
switch(theNode->operation)
{
case Keyword::UnitPercent: //Should probably do something with this...
case Keyword::UnitStandard:
case Keyword::ScalarType: return theNode->val;
case Keyword::ScopedValueKey:
assert(theContext);
return theContext->getEnv()->getInteger(theContext, theNode->extVal);
case Keyword::OperatorPlus:
return left + right;
case Keyword::OperatorMinus:
return left - right;
case Keyword::OperatorTimes:
return left * right;
case Keyword::OperatorDivide:
return left / right;
default: throw std::logic_error("Unsupported operator in solveNode. This can't happen.");
}
}
double Expression::solveForUnknownNode(const ExpressionNode* theNode,
DisplayItem* theContext,
const double requiredResult) const
{
if(theNode->weakTerm)
{
assert(theContext);
theContext->getEnv()->setInteger(theContext, theNode->extVal, requiredResult);
return requiredResult;
}
if(!theNode->weakBranch)
{
std::cerr << "IVD Runtime: Attempting to solve for unknown on constant expression." << std::endl;
return 0;
}
//The compiler should reject any code that leads to this condition, so
// no real error handling.
assert(!(theNode->left.get()->weakBranch && theNode->right.get()->weakBranch));
const bool leftUnknown = theNode->left.get()->weakBranch;
const double knownValue = [&]
{
if(leftUnknown)
return solveNode(theNode->right.get(), theContext);
else
return solveNode(theNode->left.get(), theContext);
}();
ExpressionNode* weakNode = [&]
{
if(leftUnknown)
return theNode->left.get();
else
return theNode->right.get();
}();
//wheee
return solveForUnknownNode(weakNode, theContext, [&]() -> double
{
switch(theNode->operation)
{
case Keyword::OperatorPlus: return requiredResult - knownValue;
case Keyword::OperatorTimes: return requiredResult / knownValue;
case Keyword::OperatorMinus:
if(leftUnknown) return knownValue + requiredResult;
else return knownValue - requiredResult;
case Keyword::OperatorDivide:
if(leftUnknown) return knownValue * requiredResult;
else return knownValue / requiredResult;
default: throw std::logic_error("Unsupported operator in solveUnknownNode. This can't happen.");
}
}());
}
void Expression::applyToEachScopedValueKey(std::function<void (ScopedValueKey &)> fun)
{
std::function<void(ExpressionNode*)> applicator = [&](ExpressionNode* node)
{
if(node->operation == Keyword::ScopedValueKey)
node->extVal.apply(fun);
if(node->left) applicator(node->left.get());
if(node->right) applicator(node->right.get());
};
applicator(&root);
}
std::string Expression::generatePrintout()
{
return generateExpressionPrintout(root);
}
}//IVD

123
src/expression.h Normal file
View File

@ -0,0 +1,123 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef EXPRESSION_H
#define EXPRESSION_H
#include <list>
#include <map>
#include <memory>
#include <functional>
#include "statekey.h" //For ScopedValueKey
#include "keywords.h"
#include "codeposition.h"
namespace IVD { class Element; class Compiler; }
void printOutAttributes(IVD::Compiler&, IVD::Element);
namespace IVD
{
class DisplayItem;
struct ExpressionNode
{
//basic integer to work with values from keywords.h
//Accepts: Scalar, UnitPercent, UnitStandard, OperatorPlus, OperatorMinus, OperatorTimes, OperatorDivide
// and lest we forgets, ScopedValueKey
int operation;
bool weakBranch;
bool weakTerm; //Only dependency can be weak;
double val;
ScopedValueKey extVal;
ExpressionNode(): weakBranch(false), weakTerm(false) {}
ExpressionNode(const ExpressionNode& other) { this->operator=(other); }
ExpressionNode& operator=(const ExpressionNode& other)
{
operation = other.operation;
weakBranch = other.weakBranch;
weakTerm = other.weakTerm;
val = other.val;
extVal = other.extVal;
left.reset();
right.reset();
if(other.left) left = std::make_unique<ExpressionNode>(*other.left);
if(other.right) right = std::make_unique<ExpressionNode>(*other.right);
return *this;
}
RUSTUTILS_DEFINE_COMP(ExpressionNode, operation, weakBranch, weakTerm, val, extVal)
std::unique_ptr<ExpressionNode> left;
std::unique_ptr<ExpressionNode> right;
void initLeft() { left = std::make_unique<ExpressionNode>(); }
void initRight() { right = std::make_unique<ExpressionNode>(); }
std::string printoutThySelf() const;
};
class Expression
{
ExpressionNode root;
double solveNode(const ExpressionNode* theNode, DisplayItem* theContext) const;
double solveForUnknownNode(const ExpressionNode* theNode,
DisplayItem* theContext,
const double requiredResult) const;
public:
Expression() {}
Expression(const ExpressionNode& sourceRoot):
root(sourceRoot)
{}
Expression(const ExpressionNode& sourceRoot, const CodePosition codepos):
root(sourceRoot),
definedAt(codepos)
{}
//Rename this to unleash dragons.
CodePosition definedAt;
void setRootNode(const ExpressionNode& sourceRoot)
{ root = sourceRoot; }
bool checkContainsWeak() const
{ return root.weakTerm || root.weakBranch; }
double solve(DisplayItem* theContext) const
{
return solveNode(&root, theContext);
}
void solveForAndPropogateWeak(DisplayItem* theContext, const double requiredResult) const
{
solveForUnknownNode(&root, theContext, requiredResult);
}
void applyToEachScopedValueKey(std::function<void(ScopedValueKey&)> fun);
//But it doesn't compare context, eef... Context is really just a cache mechanism.
RUSTUTILS_DEFINE_COMP(Expression, root)
std::string generatePrintout();
};
typedef std::map<ValueKey, Expression> DefineContainer;
typedef std::map<ScopedValueKey, Expression> SetContainer;
}//IVD
#endif // EXPRESSION_H

3
src/geometry.h Executable file
View File

@ -0,0 +1,3 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "user_include/cpp/IVD_geometry.h"

4
src/geometryproposal.h Normal file
View File

@ -0,0 +1,4 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "user_include/cpp/IVD_geometry_proposal.h"

130
src/graph.cpp Normal file
View File

@ -0,0 +1,130 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "graph.h"
#include "keywords.h"
#include <cmath>
#include <sstream>
namespace IVD
{
namespace Animation
{
Graph::TwoSamplePoints Graph::getSamplePoints(double percent) const
{
Sample leftSample = {0, 0};
Sample rightSample = {1, 1};
for(auto it = mySamples.begin(); it != mySamples.end(); ++it)
{
auto sample = *it;
if(sample.x == percent) return {sample, sample};
if(sample.x < percent)
{
if(it + 1 != mySamples.end()) continue;
//Otherwise, we don't have a user-defined *right* weight that is as large
// as the percentage, which defaults to 1 @ 1, which is already set above.
//So we just use this one as the left weight
leftSample = leftSample;
break;
}
if(sample.x > percent)
{
rightSample = sample;
if(it == mySamples.begin())
{
//then, there is no defined weight small enough, in which case we
// define it as 0 @ 0, which is what leftWeight is already initialized to.
break;
}
--it;
leftSample = *it;
break;
}
}
return {leftSample, rightSample};
}
double Graph::getLinearWeightForPercentage(double xpos) const
{
const TwoSamplePoints points = getSamplePoints(xpos);
if(points.left.y == points.right.y)
return points.left.y;
//http://paulbourke.net/miscellaneous/interpolation/
return points.left.y * (1 - xpos) + points.right.y * xpos;
}
double Graph::getSmoothWeightForPercentage(double xpos) const
{
const TwoSamplePoints points = getSamplePoints(xpos);
if(points.left.y == points.right.y)
return points.left.y;
//Used without understanding from:
//http://paulbourke.net/miscellaneous/interpolation/
xpos = (1 - std::cos(xpos * M_PI)) / 2;
return points.left.y * (1 - xpos) + points.right.y * xpos;
}
Graph::Graph(): interpolationMode(Keyword::Linear) {}
int Graph::getInterpolatedScalarForPercentage(const int origin,
const int dest,
const double percentage) const
{
const double destWeight = [&]
{
switch(interpolationMode)
{
case Keyword::Linear: return getLinearWeightForPercentage(percentage);
case Keyword::Smooth: return getSmoothWeightForPercentage(percentage);
default: assert(false);
}
}();
const double originWeight = 1 - destWeight;
return (origin * originWeight) + (dest * destWeight);
}
std::string Graph::generatePrintout()
{
std::stringstream ss;
ss << "Mode: ";
if(interpolationMode == Keyword::Linear) ss << "Linear";
else if(interpolationMode == Keyword::Smooth) ss << "Sinusoidal";
else throw std::logic_error("Corrupt interpolation mode in Graph");
ss << ", samples: ";
for(auto it = mySamples.begin(); it != mySamples.end(); ++it)
{
const Sample sample = *it;
ss << sample.x << " @ " << sample.y << (it + 1 != mySamples.end() ? ", "
: ".");
}
return ss.str();
}
std::string Transition::generatePrintout()
{
std::stringstream ss;
ss << "Time: " << miliseconds << "ms. " << graph.generatePrintout();
return ss.str();
}
}//Animation
}//IVD

82
src/graph.h Normal file
View File

@ -0,0 +1,82 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include <vector>
#include <string>
#include "rustutils/lexcompare.h"
#include "codeposition.h"
namespace IVD
{
namespace Animation
{
class Graph
{
public:
struct Sample
{
double x;
double y; //Constantly double your weight with this one w e i r d t r i c k
RUSTUTILS_DEFINE_COMP(Sample, x, y)
};
private:
std::vector<Sample> mySamples;
struct TwoSamplePoints
{
Sample left;
Sample right;
};
int interpolationMode;
TwoSamplePoints getSamplePoints(double percent) const;
double getLinearWeightForPercentage(double percent) const;
double getSmoothWeightForPercentage(double percent) const;
public:
Graph();
Graph(const int interpolationMode): interpolationMode(interpolationMode) {}
void addSample(const double weight, const double percent)
{ mySamples.emplace_back(Sample{weight, percent}); }
void setSamples(const std::vector<Sample> samples)
{ mySamples = samples; }
int getInterpolatedScalarForPercentage(const int origin,
const int dest,
const double percentage) const;
//Rename this to unleash dragons.
CodePosition definedAt;
std::string generatePrintout();
RUSTUTILS_DEFINE_COMP(Graph, mySamples, interpolationMode, definedAt)
};
struct Transition
{
int miliseconds;
Graph graph; //Girafe, what a gaffe...
std::string generatePrintout();
RUSTUTILS_DEFINE_COMP(Transition, miliseconds, graph)
};
}//Animation
}//IVD

645
src/keywords.h Normal file
View File

@ -0,0 +1,645 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef KEYWORDS_H
#define KEYWORDS_H
#include "assert.h"
#include <vector>
#include <string>
#include <optional>
#include <map>
#include <set>
#include "attributebodytypes.h"
namespace IVD
{
namespace AttributeKey
{
enum
{
FirstAttribute = 0,
PositionWithin = FirstAttribute,
TitleText,
Text,
Font,
ImagePath,
FontSize,
TranslationO,
TranslationA,
MarginOppOut,
MarginOppIn,
MarginAdjIn,
MarginAdjOut,
PaddingOppOut,
PaddingOppIn,
PaddingAdjIn,
PaddingAdjOut,
SizeO,
SizeA,
//Properties
Orientation,
WindowState,
Visibility,
AlignAdjacent,
AlignOpposite,
OverrideFillPrecedenceAdjacent,
OverrideFillPrecedenceOpposite,
Justify,
WindowSizeStrategy,
ImageSizeProperty,
Borderless,
Resizable,
ModelOrder,
ElementColor,
FontColor,
BorderColor,
Triggers,
InduceState,
BindState,
ToggleState,
UnsetState,
TriggerState,
RadioState,
Layout,
Widget,
CellNames,
AttributeCount,
UnnaturalAttributesStart = AttributeCount,
//These are unnatural (compound) Attributes.
Margins = UnnaturalAttributesStart,
Padding,
LastUnnatturalAttribute = Padding,
LastAttribute = LastUnnatturalAttribute,
KeywordsStart = LastAttribute,
};
} //AttributeKey
namespace Keyword
{
enum
{
SchoolOperator = AttributeKey::KeywordsStart,
Dot,
Pound,
Arrow,
Colon,
Comma,
Semicolon,
OpenBracket,
CloseBracket,
OpenSquare,
CloseSquare,
OpenParen,
CloseParen,
Pass,
Clear,
Delay,
EaseIn,
EaseOut,
Model,
State,
This,
Material,
Start,
Min,
Max,
Or,
And,
Xor,
Not,
//Not exactly keywords...
ScalarType,
FloatType,
ScopedValueKey,
AttributeProperty,
UserToken,
UserString,
ColorLiteral,
EqualSign,
UnitPercent,
UnitStandard,
UnitMilliseconds,
UnitSeconds,
OperatorPlus,
OperatorMinus,
OperatorTimes,
OperatorDivide,
Expression,
Graph,
ScalarKeyword,
Normalize,
Linear,
Smooth,
Declare,
Set,
PropertiesStart
};
}//Keywords
namespace Property
{
enum
{
PropertiesStart = Keyword::PropertiesStart,
AdjacentIsHorizontal = PropertiesStart,
AdjacentIsVertical,
Greedy,
Shrinky,
Maximized,
Minimized,
Fullscreen,
FullscreenTrue,
TopDown,
BottomUp,
Inner,
Center,
Outer,
FontSans,
FontSansBold,
FontSansItalic,
FontSansBoldItalic,
FontSerif,
FontSerifBold,
FontSerifItalic,
FontSerifBoldItalic,
FontMono,
FontMonoBold,
FontMonoItalic,
FontMonoBoldItalic,
OneToUnit,
Native,
Stretch,
BestFit,
Enable,
Disable,
PropertiesEnd,
Custom = Keyword::UserToken, //Ah, cute.
};
}//Property
namespace Spec
{
enum
{
spec = Property::PropertiesEnd,
bleeding,
};
}//Spec
inline std::map<KeyType, AttributeBodyTypes> getStandardAttributes()
{
using namespace AttributeKey;
//typedef AttributeBodyTypes B;
//Start with T/F but found this more readable.
const bool X = true;
const bool O = false;
// single scoped value key
// state key list
// token list |
// token| | |
// string | | | positionWithin
// property | | | | unnatural
return {// expression| | | | |color| |
// | | | | | | | | | |
{Margins, {X, O, O, O, O, O, O, O, X, O}},
{Padding, {X, O, O, O, O, O, O, O, X, O}},
{FontSize, {X, O, O, O, O, O, O, O, O, O}},
{TranslationO, {X, O, O, O, O, O, O, O, O, O}},
{TranslationA, {X, O, O, O, O, O, O, O, O, O}},
{MarginOppOut, {X, O, O, O, O, O, O, O, O, O}},
{MarginOppIn, {X, O, O, O, O, O, O, O, O, O}},
{MarginAdjIn, {X, O, O, O, O, O, O, O, O, O}},
{MarginAdjOut, {X, O, O, O, O, O, O, O, O, O}},
{PaddingOppOut, {X, O, O, O, O, O, O, O, O, O}},
{PaddingOppIn, {X, O, O, O, O, O, O, O, O, O}},
{PaddingAdjIn, {X, O, O, O, O, O, O, O, O, O}},
{PaddingAdjOut, {X, O, O, O, O, O, O, O, O, O}},
{SizeO, {X, O, O, O, O, O, O, O, O, O}},
{SizeA, {X, O, O, O, O, O, O, O, O, O}},
{Orientation, {O, X, O, O, O, O, O, O, O, O}},
{WindowState, {O, X, O, O, O, O, O, O, O, O}},
{Visibility, {O, X, O, O, O, O, O, O, O, O}},
{AlignAdjacent, {O, X, O, O, O, O, O, O, O, O}},
{AlignOpposite, {O, X, O, O, O, O, O, O, O, O}},
{OverrideFillPrecedenceAdjacent, {O, X, O, O, O, O, O, O, O, O}},
{OverrideFillPrecedenceOpposite, {O, X, O, O, O, O, O, O, O, O}},
{Justify, {O, X, O, O, O, O, O, O, O, O}},
{WindowSizeStrategy, {O, X, O, O, O, O, O, O, O, O}},
{ImageSizeProperty, {O, X, O, O, O, O, O, O, O, O}},
{Borderless, {O, X, O, O, O, O, O, O, O, O}},
{Resizable, {O, X, O, O, O, O, O, O, O, O}},
{ModelOrder, {O, X, O, O, O, O, O, O, O, O}},
{Font, {O, X, X, O, O, O, O, O, O, O}},
{TitleText, {O, O, X, O, O, O, X, O, O, O}},
{Text, {O, O, X, O, O, O, X, O, O, O}},
{ImagePath, {O, O, X, O, O, O, O, O, O, O}},
{Layout, {O, O, O, X, O, O, O, O, O, O}},
{Widget, {O, O, O, X, O, O, O, O, O, O}},
{CellNames, {O, O, O, O, X, O, O, O, O, O}},
{Triggers, {O, O, O, O, O, X, O, O, O, O}},
{InduceState, {O, O, O, O, O, X, O, O, O, O}},
{BindState, {O, O, O, O, O, X, O, O, O, O}},
{ToggleState, {O, O, O, O, O, X, O, O, O, O}},
{UnsetState, {O, O, O, O, O, X, O, O, O, O}},
{TriggerState, {O, O, O, O, O, X, O, O, O, O}},
{RadioState, {O, O, O, O, O, X, O, O, O, O}},
{ElementColor, {O, O, O, O, O, O, O, X, O, O}},
{FontColor, {O, O, O, O, O, O, O, X, O, O}},
{BorderColor, {O, O, O, O, O, O, O, X, O, O}},
{PositionWithin, {O, O, O, O, O, O, O, O, O, X}},
};
}
//Vector because the order is important
inline std::map<int, std::vector<int>> getNaturalKeysToUnnaturalKeyMap()
{
using namespace AttributeKey;
return {
{Margins, {MarginOppOut, MarginOppIn, MarginAdjIn, MarginAdjOut}},
{Padding, {PaddingOppOut, PaddingOppIn, PaddingAdjIn, PaddingAdjOut}},
};
}
inline std::map<int, std::set<int>> getAttributeKeyToValidPropertyList()
{
const std::initializer_list<int> fillprecedenceSet = {Property::Greedy, Property::Shrinky};
const std::initializer_list<int> booleanSet = {Property::Enable, Property::Disable};
const std::initializer_list<int> alignSet = {Property::Inner, Property::Center, Property::Outer};
return {
{AttributeKey::Font,
{Property::FontSans,
Property::FontSansBold,
Property::FontSansItalic,
Property::FontSansBoldItalic,
Property::FontSerif,
Property::FontSerifBold,
Property::FontSerifItalic,
Property::FontSerifBoldItalic,
Property::FontMono,
Property::FontMonoBold,
Property::FontMonoItalic,
Property::FontMonoBoldItalic}},
{AttributeKey::Orientation,
{Property::AdjacentIsHorizontal,
Property::AdjacentIsVertical}},
{AttributeKey::OverrideFillPrecedenceAdjacent,
fillprecedenceSet},
{AttributeKey::OverrideFillPrecedenceOpposite,
fillprecedenceSet},
{AttributeKey::WindowState,
{Property::Maximized,
Property::Minimized,
Property::Fullscreen,
Property::FullscreenTrue}},
{AttributeKey::Visibility,
{Property::Enable,
Property::Disable}},
{AttributeKey::AlignAdjacent,
alignSet},
{AttributeKey::AlignOpposite,
alignSet},
{AttributeKey::Justify,
alignSet},
{AttributeKey::WindowSizeStrategy,
{Property::TopDown,
Property::BottomUp}},
{AttributeKey::ImageSizeProperty,
{Property::OneToUnit,
Property::Native,
Property::Stretch,
Property::BestFit}},
{AttributeKey::Borderless,
booleanSet},
{AttributeKey::Resizable,
booleanSet},
{AttributeKey::ModelOrder,
booleanSet},
};
}
inline std::map<std::string, int> getTokenToSymbolMap()
{
return {{"@", Keyword::SchoolOperator},
{".", Keyword::Dot},
{"#", Keyword::Pound},
{"->", Keyword::Arrow},
{":", Keyword::Colon},
{",", Keyword::Comma},
{";", Keyword::Semicolon},
{"{", Keyword::OpenBracket},
{"}", Keyword::CloseBracket},
{"[", Keyword::OpenSquare},
{"]", Keyword::CloseSquare},
{"(", Keyword::OpenParen},
{")", Keyword::CloseParen},
{"=", Keyword::EqualSign},
{"%", Keyword::UnitPercent},
{"u", Keyword::UnitStandard},
{"ms", Keyword::UnitMilliseconds},
{"milliseconds", Keyword::UnitMilliseconds},
{"sec", Keyword::UnitSeconds},
{"seconds", Keyword::UnitSeconds},
{"+", Keyword::OperatorPlus},
{"-", Keyword::OperatorMinus},
{"*", Keyword::OperatorTimes},
{"/", Keyword::OperatorDivide},
{"expression", Keyword::Expression},
{"graph", Keyword::Graph},
{"scalar", Keyword::ScalarKeyword},
{"linear", Keyword::Linear},
{"smooth", Keyword::Smooth},
{"declare", Keyword::Declare},
{"set", Keyword::Set},
{"normalize", Keyword::Normalize},
{"pass", Keyword::Pass},
{"clear", Keyword::Clear},
{"delay", Keyword::Delay},
{"ease-in", Keyword::EaseIn},
{"ease-out", Keyword::EaseOut},
{"model", Keyword::Model},
{"state", Keyword::State},
{"this", Keyword::This},
{"material", Keyword::Material},
{"start", Keyword::Start},
{"min", Keyword::Min},
{"max", Keyword::Max},
//Bitwise gamgee
{"or", Keyword::Or},
{"|", Keyword::Or},
{"and", Keyword::And},
{"&", Keyword::And},
{"xor", Keyword::Xor},
{"!=", Keyword::Xor},
{"not", Keyword::Not},
{"!", Keyword::Not},
{"position-within", AttributeKey::PositionWithin},
{"title-text", AttributeKey::TitleText},
{"text", AttributeKey::Text},
{"font", AttributeKey::Font},
{"image-path", AttributeKey::ImagePath},
{"font-size", AttributeKey::FontSize},
{"orientation", AttributeKey::Orientation},
{"trans-o", AttributeKey::TranslationO},
{"trans-a", AttributeKey::TranslationA},
{"trans-y", AttributeKey::TranslationO},
{"trans-x", AttributeKey::TranslationA},
{"size-o", AttributeKey::SizeO},
{"size-a", AttributeKey::SizeA},
{"size-y", AttributeKey::SizeO},
{"size-x", AttributeKey::SizeA},
{"height", AttributeKey::SizeO},
{"width", AttributeKey::SizeA},
{"margin-o-out", AttributeKey::MarginOppOut},
{"margin-o-in", AttributeKey::MarginOppIn},
{"margin-a-in", AttributeKey::MarginAdjIn},
{"margin-a-out", AttributeKey::MarginAdjOut},
{"margin-bottom", AttributeKey::MarginOppOut},
{"margin-top", AttributeKey::MarginOppIn},
{"margin-left", AttributeKey::MarginAdjIn},
{"margin-right", AttributeKey::MarginAdjOut},
{"padding-o-out", AttributeKey::PaddingOppOut},
{"padding-o-in", AttributeKey::PaddingOppIn},
{"padding-a-in", AttributeKey::PaddingAdjIn},
{"padding-a-out", AttributeKey::PaddingAdjOut},
{"padding-bottom", AttributeKey::PaddingOppOut},
{"padding-top", AttributeKey::PaddingOppIn},
{"padding-left", AttributeKey::PaddingAdjIn},
{"padding-right", AttributeKey::PaddingAdjOut},
{"window-state", AttributeKey::WindowState},
{"visibility", AttributeKey::Visibility},
{"align-a", AttributeKey::AlignAdjacent},
{"align-o", AttributeKey::AlignOpposite},
{"align-x", AttributeKey::AlignAdjacent},
{"align-y", AttributeKey::AlignOpposite},
{"fill-precedence-adjacent", AttributeKey::OverrideFillPrecedenceAdjacent},
{"fill-precedence-opposite", AttributeKey::OverrideFillPrecedenceOpposite},
{"fill-precedence-a", AttributeKey::OverrideFillPrecedenceAdjacent},
{"fill-precedence-o", AttributeKey::OverrideFillPrecedenceOpposite},
{"fill-precedence-x", AttributeKey::OverrideFillPrecedenceAdjacent},
{"fill-precedence-y", AttributeKey::OverrideFillPrecedenceOpposite},
{"justify", AttributeKey::Justify},
{"window-size-strategy", AttributeKey::WindowSizeStrategy},
{"borderless", AttributeKey::Borderless},
{"resizable", AttributeKey::Resizable},
{"model-order", AttributeKey::ModelOrder},
{"color", AttributeKey::ElementColor},
{"font-color", AttributeKey::FontColor},
{"border-color", AttributeKey::BorderColor},
{"induce-state", AttributeKey::InduceState},
{"bind-state", AttributeKey::BindState},
{"toggle-state", AttributeKey::ToggleState},
{"unset-state", AttributeKey::UnsetState},
{"trigger-state", AttributeKey::TriggerState},
{"radio-state", AttributeKey::RadioState},
{"trigger", AttributeKey::Triggers},
{"layout", AttributeKey::Layout},
{"widget", AttributeKey::Widget},
{"cell-names", AttributeKey::CellNames},
//Unnaturals
{"margin", AttributeKey::Margins},
{"padding", AttributeKey::Padding},
//Property values
{"adjacent-is-horizontal", Property::AdjacentIsHorizontal},
{"adjacent-is-vertical", Property::AdjacentIsVertical},
{"greedy", Property::Greedy},
{"shrinky", Property::Shrinky},
{"maximize", Property::Maximized},
{"minimize", Property::Minimized},
{"fullscreen", Property::Fullscreen},
{"fullscreen-true", Property::FullscreenTrue},
{"top-down", Property::TopDown},
{"bottom-up", Property::BottomUp},
{"align-inner", Property::Inner},
{"align-center", Property::Center},
{"align-outer", Property::Outer},
{"align-left", Property::Inner},
{"align-right", Property::Outer},
{"align-top", Property::Inner},
{"align-bottom", Property::Outer},
{"sans", Property::FontSans},
{"sans-bold", Property::FontSansBold},
{"sans-italic", Property::FontSansItalic},
{"sans-bold-italic", Property::FontSansBoldItalic},
{"sans-italic-bold", Property::FontSansBoldItalic},
{"serif", Property::FontSerif},
{"serif-bold", Property::FontSerifBold},
{"serif-italic", Property::FontSerifItalic},
{"serif-bold-italic", Property::FontSerifBoldItalic},
{"serif-italic-bold", Property::FontSerifBoldItalic},
{"mono", Property::FontMono},
{"mono-bold", Property::FontMonoBold},
{"mono-italic", Property::FontMonoItalic},
{"mono-bold-italic", Property::FontMonoBoldItalic},
{"mono-italic-bold", Property::FontMonoBoldItalic},
{"one-to-unit", Property::OneToUnit},
{"native", Property::Native},
{"stretch", Property::Stretch},
{"best-fit", Property::BestFit},
{"enable", Property::Enable},
{"disable", Property::Disable},
{"spec", Spec::spec},
{"bleeding", Spec::bleeding},
};
}
inline std::set<int> getDelimitingSymbolSet()
{
return {Keyword::SchoolOperator,
Keyword::Dot,
Keyword::Pound,
Keyword::Arrow,
Keyword::Colon,
Keyword::Comma,
Keyword::Semicolon,
Keyword::OpenBracket,
Keyword::CloseBracket,
Keyword::OpenSquare,
Keyword::CloseSquare,
Keyword::OpenParen,
Keyword::CloseParen,
Keyword::Not,
};
}
//------------------------------------Helpers below, all syntax defined above
inline std::map<int, std::string> getSymbolToTokenMap()
{
std::map<int, std::string> result;
auto aye = getTokenToSymbolMap();
//This will overwrite aliases :/
for(auto pair : aye)
result[pair.second] = pair.first;
return result;
}
}//IVD
#endif // KEYWORDS_H

View File

@ -0,0 +1,59 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "referenceattribute.h"
#include "rustutils/routine.h"
namespace IVD
{
void ReferenceAttribute::derive(const ReferenceAttribute& other)
{
//This seems backwards for the runtime... Should just copy-in the cleared
// attribute if we're not in mode-derive?
// Shouldn't this be read ther otherway around? TODO
if(clear) return;
if(!other.active) return;
active = true;
auto mergeHelper = [](auto& mine, const auto& other)
{
if(!mine) mine = other;
};
mergeHelper(property, other.property);
mergeHelper(starting, other.starting);
mergeHelper(min, other.min);
mergeHelper(max, other.max);
mergeHelper(expr, other.expr);
mergeHelper(color, other.color);
mergeHelper(literal, other.literal);
mergeHelper(singleKey, other.singleKey);
mergeHelper(delay, other.delay);
mergeHelper(ease, other.ease);
RustUtils::Routine::appendContainer(keys, other.keys);
RustUtils::Routine::appendContainer(literalList, other.literalList);}
void ReferenceAttribute::applyToEachScopedValueKey(std::function<void (ScopedValueKey&)> fun)
{
auto guard = [&](std::optional<Expression>& optExpr)
{
if(optExpr) optExpr->applyToEachScopedValueKey(fun);
};
guard(starting);
guard(min);
guard(max);
guard(expr);
for(ScopedValueKey& key : keys) fun(key);
if(singleKey) fun(*singleKey);
}
}//IVD

41
src/referenceattribute.h Normal file
View File

@ -0,0 +1,41 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include "expression.h"
#include "color.h"
#include "graph.h"
namespace IVD
{
struct ReferenceAttribute
{
bool active = false;
bool clear = false;
bool stateModifierAttr = false;
std::optional<int> property;
std::optional<Expression> starting;
std::optional<Expression> min;
std::optional<Expression> max;
std::optional<Expression> expr;
std::optional<Color> color;
std::optional<std::string> literal;
std::vector<std::string> literalList; //Don't be such a literalist GODDDDDDDD
std::optional<ScopedValueKey> singleKey;
std::vector<ScopedValueKey> keys;
std::optional<int> delay;
std::optional<Animation::Transition> ease;
void derive(const ReferenceAttribute& other);
void applyToEachScopedValueKey(std::function<void (ScopedValueKey &)> fun);
};
}//IVD

View File

@ -0,0 +1,42 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "referenceattributeset.h"
namespace IVD
{
void ReferenceAttributeSet::mergeModifiers(const ReferenceAttributeSet& other)
{
//declares are always on the default state, but since this method
// is used to merge classes as well as states, we just merge declares
// here, and rely on the compiler to prevent declares from being added
// to attribute sets that refer to a different state.
for(auto pair : other.declareModifiers)
declareModifiers[pair.first] = pair.second;
for(auto pair : other.setModifiers)
setModifiers[pair.first] = pair.second;
}
void ReferenceAttributeSet::deriveFrom(const ReferenceAttributeSet& other)
{
mergeModifiers(other);
for(int key = 0; key != AttributeKey::AttributeCount; ++key)
attr[key].derive(other.attr[key]);
}
void ReferenceAttributeSet::applyToEachScopedValueKey(std::function<void (ScopedValueKey &)> fun)
{
for(auto& it : declareModifiers)
it.second.applyToEachScopedValueKey(fun);
for(auto& it : setModifiers)
it.second.applyToEachScopedValueKey(fun);
for(int i = 0; i != AttributeKey::AttributeCount; ++i)
attr[i].applyToEachScopedValueKey(fun);
}
}//IVD

View File

@ -0,0 +1,49 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#pragma once
#include <array>
#include "keywords.h"
#include "referenceattribute.h"
namespace IVD
{
struct ReferenceAttributeSet
{
typedef std::map<ValueKey, Expression> DeclareModifierMap;
typedef std::map<ScopedValueKey, Expression> SetModifierMap;
std::vector<ReferenceAttribute> attr;
//Set can modify but not observe.
//Declare can observe, and provide a filter for modification,
DeclareModifierMap declareModifiers;
SetModifierMap setModifiers;
ReferenceAttributeSet(const int attrCount):
attr(attrCount)
{}
int size() const
{ return attr.size(); }
ReferenceAttribute getAttribute(const int key) const
{ return attr[key]; }
void insertAttribute(ReferenceAttribute theAttribute, const int key)
{ attr[key] = theAttribute; }
void insertDeclareModifier(ValueKey target, Expression expr)
{ declareModifiers[target] = expr; }
void insertSetModifier(ScopedValueKey target, Expression expr)
{ setModifiers[target] = expr; }
void mergeModifiers(const ReferenceAttributeSet& other);
void deriveFrom(const ReferenceAttributeSet& other);
void applyToEachScopedValueKey(std::function<void(ScopedValueKey&)> fun);
};
}//IVD

268
src/runtimeattribute.cpp Normal file
View File

@ -0,0 +1,268 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "runtimeattribute.h"
#include "rustutils/routine.h"
#include "runtimeattributeset.h"
#include <reprodyne.h>
namespace IVD
{
std::optional<double> RuntimeAttribute::getValue(DisplayItem* theContext) const
{
//For simplicity's sake, min/max are used to round off observed values,
// but are not calculated in back-propogation, and thus there is no
// back and forth. An attribute simply takes in a suggested value,
// calculates it, and when you need to actually observe the value
// (which is done in a seperate function from from set), the bounds
// are checked, and rounded, and that should be enough. If there are
// min/max constraints on both attributes, it gets complicated real quick,
// and I haven't found a good use case for that. min/max is good for
// making sure a scalar is within range, but I see no reason why
// the two values must agree if they both have constraints.
//tl;dl local attribute min/max takes precedence over everything else.
std::optional<double> value;
if(!expr) return value;
value = expr->solve(theContext);
if(min)
{
const double computedMin = min->solve(theContext);
if(computedMin > value) value = computedMin;
}
if(max)
{
const double computedMax = max->solve(theContext);
if(computedMax < value) value = computedMax;
}
return value;
}
void RuntimeAttribute::setValue(const double proposed, DisplayItem* theContext)
{
starting = nullptr;
if(expr) expr->solveForAndPropogateWeak(theContext, proposed);
}
template<typename T, typename TO>
static void mergeHelper(T& mine, const TO& other)
{
if(other) mine = &other;
}
template<typename T, typename TO>
static void mergeHelper(T& mine, const std::optional<TO>& other)
{
if(other) mine = &*other;
}
template<typename T, typename TO>
static void mergeHelper(T& mine, const std::vector<TO>& other)
{
if(other.size()) mine = other;
}
void RuntimeAttribute::merge(const ReferenceAttribute& other)
{
mergeHelper(property, other.property);
mergeHelper(starting, other.starting);
mergeHelper(min, other.min);
mergeHelper(max, other.max);
mergeHelper(expr, other.expr);
mergeHelper(color, other.color);
mergeHelper(literal, other.literal);
mergeHelper(singleKey, other.singleKey);
RustUtils::Routine::appendContainer(keys, other.keys);
RustUtils::Routine::appendContainer(literalList, other.literalList);
}
void RuntimeAttribute::reset()
{
clear = nullptr;
property = nullptr;
starting = min = max = expr = nullptr;
color = nullptr;
literal = nullptr;
literalList.clear();
singleKey = nullptr;
keys.clear();
}
const RuntimeAttribute& AnimatableAttribute::getCorrectRTA() const
{
return lastRatio == 1 ? currentRTA
: previousRTA;
}
void AnimatableAttribute::animationTick()
{
double changeRatio = 0;
if(!animationStart)
{
animationStart = std::chrono::steady_clock::now();
lastRatio = 0; //todo
}
else
{
//Guaranteed to only be ease or delay.
const auto maxDuration = std::chrono::milliseconds(ease ? ease->miliseconds
: *delay);
const auto startTimePoint = (*animationStart);
const auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - startTimePoint);
if(elapsedTime >= maxDuration)
{
changeRatio = 1;
}
else if(ease) //Delay kind of takes care of itself~
{
const double e = elapsedTime.count();
const double m = maxDuration.count();
changeRatio = e / m;
}
}
//This block is for Reprodyne stuff
{
//Scope to displayitem itself because our pointer should be able to change.
std::string key;
key = std::to_string(myAttributeKey);
key += "-:-animation-ratio";
//Needs to intercept time or we can't validate the correct calculation of the
// change ratio....... TODO
changeRatio = reprodyne_intercept_double(revealContext(), key.c_str(), changeRatio);
}
if(lastRatio != changeRatio)
{
lastRatio = changeRatio;
signalChangedAttribute(this);
}
if(lastRatio == 1) quitAnimation();
}
void AnimatableAttribute::beginAttributeRecompute()
{
//We store this not only for animation, but for checking if anything
// changed in commitAttributeRecompute()
checkpointRTA = currentRTA;
ease = nullptr;
delay = nullptr;
active = false;
currentRTA.reset();
}
void AnimatableAttribute::merge(const ReferenceAttribute& ref)
{
if(currentRTA.checkClear() || !ref.active) return;
active = true;
if(ref.ease) ease = &*ref.ease;
if(ref.delay) delay = &*ref.delay;
currentRTA.merge(ref);
}
void AnimatableAttribute::commitAttributeRecompute()
{
//This doesn't check our full configuration (clear........)
if(checkpointRTA == currentRTA) return;
previousRTA = checkpointRTA;
if(ease || delay)
{
lastRatio = 0;
requestAnimationTicker(this);
}
else
{
lastRatio = 1;
cancelAnimationTicker(this); //Why not call "quitAnimation"? TODO
}
signalChange();
}
std::optional<double> AnimatableAttribute::getValue() const
{
std::optional<double> result;
if(lastRatio != 1)
{
auto optionalOrigin = previousRTA.getValue(theContext);
auto optionalDest = currentRTA.getValue(theContext);
if(!optionalOrigin || !optionalDest) return result; //wtf condition
return ease->graph.getInterpolatedScalarForPercentage(*optionalOrigin,
*optionalDest,
lastRatio);
}
else return currentRTA.getValue(theContext);
}
std::optional<int> AnimatableAttribute::getProperty() const
{
const int* i = getCorrectRTA().property;
return i ? *i
: std::optional<int>();
}
std::vector<std::string> AnimatableAttribute::getLiteralList() const
{
return getCorrectRTA().literalList;
}
std::optional<ScopedValueKey> AnimatableAttribute::getSingleValueKey() const
{
const ScopedValueKey* i = getCorrectRTA().singleKey;
return i ? *i
: std::optional<ScopedValueKey>();
}
std::vector<ScopedValueKey> AnimatableAttribute::getValueKeyList() const
{
return getCorrectRTA().keys;
}
std::optional<std::string> AnimatableAttribute::getUserToken() const
{
const std::string* i = getCorrectRTA().literal;
return i ? *i
: std::optional<std::string>();
}
std::optional<Color> AnimatableAttribute::getColor() const
{
const Color* i = getCorrectRTA().color;
return i ? *i
: std::optional<Color>();
}
} //IVD

187
src/runtimeattribute.h Normal file
View File

@ -0,0 +1,187 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef ATTRIBUTE_H
#define ATTRIBUTE_H
#include <optional>
#include <chrono>
#include "rustutils/lexcompare.h"
#include "rustutils/easyuniquepointer.h"
#include "color.h"
#include "expression.h"
#include "graph.h"
#include "referenceattribute.h"
namespace IVD
{
class AnimatableAttribute;
class RuntimeAttribute //1224 bytes before 632 after~ NOW 248... 96... 128 Fack
{
//Doesn't really need to be a friend it can just be all public because AnimatableAttribute
// is all that uses it... Or just make it internal, rename to data, save "runtimeAttribute"
// as the public class name.....
friend class AnimatableAttribute;
int myAttributeKey = -1; //TODO: make constant
//No "active" because if this exists, it's active
const bool* clear = nullptr;
const int* property = nullptr;
const Expression* starting = nullptr;
const Expression* min = nullptr;
const Expression* max = nullptr;
const Expression* expr = nullptr;
const Color* color = nullptr;
const std::string* literal = nullptr;
std::vector<std::string> literalList; //Don't be such a literalist GODDDDDDDD
const ScopedValueKey* singleKey = nullptr;
std::vector<ScopedValueKey> keys;
public:
void initializeTransitionSystem(const int key)
{ myAttributeKey = key; }
void reset();
std::optional<double> getValue(DisplayItem* theContext) const;
void setValue(const double proposed, DisplayItem* theContext);
void merge(const ReferenceAttribute& other);
void applyToEachScopedValueKey(std::function<void(ScopedValueKey&)> fun);
auto getScopedValueKeys()
{ return keys; }
bool checkClear()
{ return clear; }
RUSTUTILS_DEFINE_COMP(RuntimeAttribute,
clear,
property,
starting, min, max, expr,
color,
literal,
literalList,
singleKey,
keys)
};
class AnimatableAttribute //440 byte struct replacing a 1224 byte struct, noice
{
int myAttributeKey = -1;
DisplayItem* theContext = nullptr;
bool active = false;
RuntimeAttribute previousRTA; //Lol RTA
RuntimeAttribute currentRTA;
//We need a third copy in case the values are the same as the current and as such
// the recompute is redundant. (TODO: Should recomputes just not do this, period?)
RuntimeAttribute checkpointRTA;
//These point to the (current) reference attribute
const int* delay = nullptr; //Yeah it's silly indirection for an integer but it's consistent.
const Animation::Transition* ease = nullptr;
std::optional<std::chrono::time_point<std::chrono::steady_clock>> animationStart;
//lastRatio == 1 means animation is finished.
double lastRatio = 1;
std::function<void(AnimatableAttribute*)> signalChangedAttribute;
std::function<void(AnimatableAttribute*)> changeAcceptor;
std::function<void(AnimatableAttribute*)> requestAnimationTicker;
std::function<void(AnimatableAttribute*)> cancelAnimationTicker;
const RuntimeAttribute& getCorrectRTA() const;
public:
void init(DisplayItem* thethecontext, const int key)
{
theContext = thethecontext;
myAttributeKey = key;
}
bool checkActive()
{ return active; }
bool thisIsAhackButCheckIfThereIsDelay()
{ return delay; }
DisplayItem* revealContext()
{ return theContext; }
void setSignalChangedAttribute(std::function<void(AnimatableAttribute*)> fun)
{ signalChangedAttribute = fun; }
void setChangeAcceptor(std::function<void(AnimatableAttribute*)> fun)
{ changeAcceptor = fun; }
void setAnimationTickRequester(std::function<void(AnimatableAttribute*)> fun)
{ requestAnimationTicker = fun; } //I mean, it *is* a lot of fun!
void setCancelAnimationTicker(std::function<void(AnimatableAttribute*)> fun)
{ cancelAnimationTicker = fun; }
void animationTick();
void quitAnimation()
{
cancelAnimationTicker(this);
animationStart.reset();
}
void executeChangeAcceptor()
{
assert(changeAcceptor); //Because assert gives line numbers and a bad function call exception does not.
changeAcceptor(this);
}
void signalChange()
{
assert(signalChangedAttribute);
signalChangedAttribute(this);
}
RuntimeAttribute& getCurrent()
{ return currentRTA; }
void beginAttributeRecompute();
void merge(const ReferenceAttribute& ref);
void commitAttributeRecompute();
//data interface
void setValue(const double proposed)
{ currentRTA.setValue(proposed, theContext); }
std::optional<double> getValue() const;
bool checkExprIsConst() const
{
auto expr = currentRTA.expr;
return expr ? !expr->checkContainsWeak()
: true;
}
std::optional<int> getProperty() const;
std::vector<std::string> getLiteralList() const;
std::optional<ScopedValueKey> getSingleValueKey() const;
std::vector<ScopedValueKey> getValueKeyList() const;
std::optional<std::string> getUserToken() const;
std::optional<Color> getColor() const;
};
}//IVD
#endif // ATTRIBUTE_H

114
src/runtimeattributeset.cpp Normal file
View File

@ -0,0 +1,114 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include <unordered_set>
#include "runtimeattributeset.h"
#include "environment.h"
namespace IVD
{
RuntimeAttributeSet::RuntimeAttributeSet(DisplayItem* context, const ReferenceAttributeSet& initializerSet):
myContext(context),
attrs(initializerSet.size(), AnimatableAttribute())
{
//We only want the initializer set to initialize the state changing attribute set.
//Otherwise we just need the attr size
assert(myContext);
for(int key = 0; key != initializerSet.size(); ++key)
{
attrs.at(key).init(context, key);
context->getEnv()->setupEnvironmentCallbacksOnAttributeForKey(&attrs.at(key), key);
if(initializerSet.attr[key].stateModifierAttr)
stateChangingAttributes.push_back(&attrs.at(key));
}
}
void RuntimeAttributeSet::applyToEachAttribute(std::function<void (AnimatableAttribute&)> fun)
{
for(int key = 0; key != AttributeKey::AttributeCount; ++key)
fun(attrs[key]);
}
void RuntimeAttributeSet::beginAttributeSetRecompute()
{
declareModifiers = nullptr;
setModifiers.clear();
applyToEachAttribute([&](AnimatableAttribute& a)
{ a.beginAttributeRecompute(); });
}
void RuntimeAttributeSet::mergeIn(const ReferenceAttributeSet& other)
{
//We just pretend that the data is clean and that only the first
// reference set has declare modifiers...
if(!declareModifiers) declareModifiers = &other.declareModifiers;
setModifiers.push_back(&other.setModifiers);
for(int key = 0; key != AttributeKey::AttributeCount; ++key)
attrs[key].merge(other.attr[key]);
}
void RuntimeAttributeSet::commitAttributeSetRecompute()
{
applyToEachAttribute([&](AnimatableAttribute& a)
{ a.commitAttributeRecompute(); });
}
void RuntimeAttributeSet::executeStateChangers()
{
//We only update state modifiers every time the attribute set is updated. Not triggers.
//TODO unclear code...
for(auto attr : stateChangingAttributes)
{
//Not considered for quick, logical things
//TODOOOOOOOO
if(attr->thisIsAhackButCheckIfThereIsDelay()) continue;
attr->executeChangeAcceptor();
}
}
void RuntimeAttributeSet::fireSets()
{
std::unordered_set<const Expression*> firedExpressions;
//This is still O(N), the inner loop is just a sub-range don't lose your shit.
for(auto& innerSet : setModifiers)
{
for(auto& pair : *innerSet)
{
const Expression* expr = &pair.second;
if(firedExpressions.count(expr)) continue;
myContext->getEnv()->setInteger(myContext, pair.first, expr->solve(myContext));
firedExpressions.insert(expr);
}
}
}
std::optional<double> RuntimeAttributeSet::getDeclaredInt(ValueKey key)
{
if(!declareModifiers->count(key)) return std::optional<double>();
return declareModifiers->at(key).solve(myContext);
}
void RuntimeAttributeSet::setDeclaredInt(const ValueKey key, const double proposed)
{
assert(declareModifiers->count(key));
const Expression& expr = declareModifiers->at(key);
assert(expr.checkContainsWeak());
expr.solveForAndPropogateWeak(myContext, proposed);
}
}//IVD

93
src/runtimeattributeset.h Normal file
View File

@ -0,0 +1,93 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef ATTRIBUTESET_H
#define ATTRIBUTESET_H
#include <map>
#include <tuple>
#include <vector>
#include <optional>
#include <functional>
#include <array>
#include "rustutils/routine.h"
#include "referenceattributeset.h"
#include "runtimeattribute.h"
#include "statekey.h"
#include "keywords.h"
namespace IVD
{
class DisplayItem;
class RuntimeAttributeSet
{
friend class Compiler;
friend void ::printOutAttributes(IVD::Compiler&, IVD::Element);
DisplayItem* myContext;
std::vector<AnimatableAttribute> attrs;
//These are pointers into attrs, which is initialized to it's
// final size so it should never invalidate.
std::vector<AnimatableAttribute*> stateChangingAttributes;
const ReferenceAttributeSet::DeclareModifierMap* declareModifiers;
std::vector<const ReferenceAttributeSet::SetModifierMap*> setModifiers;
void applyToEachAttribute(std::function<void (AnimatableAttribute&)> fun);
public:
RuntimeAttributeSet(): myContext(nullptr) {}
RuntimeAttributeSet(DisplayItem* context, const ReferenceAttributeSet& initializerSet);
void beginAttributeSetRecompute();
void mergeIn(const ReferenceAttributeSet& other);
void commitAttributeSetRecompute();
void executeStateChangers();
void fireSets();
std::optional<double> getDeclaredInt(ValueKey key);
void setDeclaredInt(const ValueKey key, const double proposed);
bool checkActive(const int key)
{ return attrs[key].checkActive(); }
std::optional<double> getInt(const int key) const
{ return attrs[key].getValue(); }
void setInteger(const int key, const double proposed)
{ attrs[key].setValue(proposed); }
bool isConst(const int key)
{ return attrs[key].checkExprIsConst(); }
//These don't necessarily have to filter like this, but it's consistent
// and it might make sense someday to filter them with special declare types
// or something.
std::optional<int> getProperty(const int key)
{ return attrs[key].getProperty(); }
std::vector<std::string> getLiteralList(const int key)
{ return attrs[key].getLiteralList(); }
std::optional<ScopedValueKey> getSingleValueKey(const int key)
{ return attrs[key].getSingleValueKey(); }
std::vector<ScopedValueKey> getValueKeyList(const int key) //Optional would be more consistent...
{ return attrs[key].getValueKeyList(); }
std::optional<std::string> getUserToken(const int key) const
{ return attrs[key].getUserToken(); }
std::optional<Color> getColor(const int key)
{ return attrs[key].getColor(); }
};
}//Attributes
#endif // ATTRIBUTESET_H

239
src/shaping/line.h Normal file
View File

@ -0,0 +1,239 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef LINE_H
#define LINE_H
/*
namespace IVD
{
namespace Shaping
{
template<typename Cont>
Dimens line(Cont theMaterials, const GeometryProposal proposal, const Angle Adjacent)
{
const int CellCount = theMaterials.size();
const Angle Opposite = (Adjacent == Angle::Horizontal) ? Angle::Vertical
: Angle::Horizontal;
const int AvailableAdjacentSpace = proposal.proposedDimensions.get(Adjacent);
const int AvailableOppositeSpace = proposal.proposedDimensions.get(Opposite);
std::vector<Material*> greedy;
std::vector<Material*> shrinky;
for(Material* material : theMaterials)
{
const FillPrecedence Pref = material->getFillPrecedenceForAngle(Adjacent);
if(Pref == FillPrecedence::Greedy)
greedy.push_back(material);
if(Pref == FillPrecedence::Shrinky)
shrinky.push_back(material);
}
int usedAdjacentSpace;
int usedOppositeSpace;
auto resetWorkingAdjacent = [&]
{ usedAdjacentSpace = 0; };
auto resetWorkingOpposite = [&]
{ usedOppositeSpace = 0; };
auto resetWorkingDimensions = [&]
{
resetWorkingAdjacent();
resetWorkingOpposite();
};
resetWorkingDimensions();
bool invalidOpposite = false;
auto applyAdjacentFormulaToChild = [&](Material* material,
GeometryProposal childProposal,
std::function<int(Material*)> adjacentFormula)
{
//Please leave this here I'm sick of adding it back for debugging
const auto proposedAdjacentSize = adjacentFormula(material);
//----------Adjacent
childProposal.proposedDimensions.get(Adjacent) = proposedAdjacentSize;
material->shape(childProposal);
const Dimens childDimens = material->getViewport().d;
usedAdjacentSpace += childDimens.get(Adjacent);
//----------Opposite
const int UsedOppositeThisRound = childDimens.get(Opposite);
if(UsedOppositeThisRound > usedOppositeSpace)
usedOppositeSpace = UsedOppositeThisRound;
if(AvailableOppositeSpace != UsedOppositeThisRound)
invalidOpposite = true;
//----------
assert(childProposal.verifyCompliance(childDimens));
};
auto applyToSet = [&](const std::vector<Material*>& items,
GeometryProposal childProposal,
std::function<int(Material*)> formula)
{
for(Material* material : items)
applyAdjacentFormulaToChild(material, childProposal, formula);
};
auto adjustOpposite = [&]()
{
if(!invalidOpposite) return;
//The problem here is that one widget might have actually used all of the opposite
// correctly, while a different one shrank. So really we need a better test to see
// if the widgets didn't all use the same space!!! : TODO, is this still a thing???
// I don't understand if it's still a problem, doesn't seem like it???
resetWorkingAdjacent();
//We don't discriminate between greedy and shrinky this time, because
// we're only looking to update the child opposites and perhaps
// shrink our adjacent.
for(Material* material : theMaterials)
{
//We've already determined the opposite, so lock both opposite shrink and expand.
//If the opposite is larger, we might recover some adjacent space,
// so we don't shrink lock that dimension.
//If the opposite is smaller, do we need to lock adjacent shrinking?
//I can't think of anything but the most contrived scenario where
// the adjacent would shrink further. But I also don't see why we
// *have to* lock it...
//(Wouldn't it shrink if we increase the opposite in a vertical text flow scenario?)
//Yeah... Lock it.
//The computed adjacent is always big enough to handle the computed opposite
// or larger. We won't be suggesting a smaller opposite, so the adjacent should
// not expand further.
GeometryProposal childProposal = proposal;
childProposal.proposedDimensions = material->getViewport().d;
childProposal.proposedDimensions.get(Opposite) = usedOppositeSpace;
childProposal.expandForAngle(Opposite) = false;
childProposal.shrinkForAngle(Opposite) = false;
childProposal.expandForAngle(Adjacent) = false;
childProposal.shrinkForAngle(Adjacent) = false;
material->shape(childProposal);
const Dimens childDimens = material->getViewport().d;
usedAdjacentSpace += childDimens.get(Adjacent);
assert(childProposal.verifyCompliance(childDimens));
}
invalidOpposite = false;
};
//Initial pass, get an idea of what the natural sizes are
{
//No explicit expand for opposite because it's taking in the
// higher up suggestion which we much obey anyway.
//Otherwise it throws off the overall opposite rule.
//If it's locked it can't legally grow!!!! Or shrink... No shrinky!
// extra space goes to the child cell anyway.
GeometryProposal childProposal = proposal;
childProposal.expandForAngle(Adjacent) = true;
childProposal.shrinkForAngle(Adjacent) = true;
auto initalCellSizeFormula = [=](Material*) -> int
{ return zeroGuard((AvailableAdjacentSpace - usedAdjacentSpace) / CellCount); };
applyToSet(shrinky, childProposal, initalCellSizeFormula);
applyToSet(greedy, childProposal, initalCellSizeFormula);
adjustOpposite();
}
//If the sizes are bad, then we adjust
if(usedAdjacentSpace > proposal.proposedDimensions.get(Adjacent))
{
if(!proposal.expandForAngleConst(Adjacent))
{
//No overflow mode, because if we can't expand, we can't expand.
//So it must go to the children. The default is for the whole thing
// to take on the full size, if you want a viewport, put this layout
// inside an item with a viewport layout.
const int cutSize = (usedAdjacentSpace - AvailableAdjacentSpace) / CellCount;
auto adjustingSizeFormula = [&](Material* material) -> int
{ return zeroGuard(material->getViewport().d.get(Adjacent) - cutSize); };
//(╯°□°)╯︵ ┻━┻
resetWorkingDimensions();
GeometryProposal childProposal = proposal;
childProposal.expandForAngle(Adjacent) = false;
applyToSet(shrinky, childProposal, adjustingSizeFormula);
applyToSet(greedy, childProposal, adjustingSizeFormula);
adjustOpposite();
}//Else is just whatev's
}
if(usedAdjacentSpace < proposal.proposedDimensions.get(Adjacent))
{
if(!proposal.shrinkForAngleConst(Adjacent) && CellCount)
{
//By default we only expand greedy. But if there is no greedy and we MUST expand...
//Actually not sure if this is ever greedy, now that I thonk abut it? Can it be? TODO
auto& theSet = greedy.size() ? greedy
: shrinky;
auto& opposet = greedy.size() ? shrinky
: greedy;
const int padSize = (AvailableAdjacentSpace - usedAdjacentSpace) / theSet.size();
resetWorkingDimensions();
auto padFormula = [&](Material* material) -> int
{ return material->getViewport().d.get(Adjacent) + padSize; };
GeometryProposal childProposal = proposal;
childProposal.shrinkForAngle(Adjacent) = false;
applyToSet(theSet, childProposal, padFormula);
//One last problem, we gotta add the opposets to the usedAdjacent and Opposite(?) spaces...
//(This could maybe be abstracted with some code from applyToSet TODO)
for(Material* m : opposet)
{
const Dimens d = m->getViewport().d;
if(d.get(Opposite) > usedOppositeSpace)
{
//Does this ever happen????? TODO
usedOppositeSpace = d.get(Opposite);
invalidOpposite = true;
}
usedAdjacentSpace += d.get(Adjacent);
}
adjustOpposite();
}
}
Dimens usedSpace;
usedSpace.get(Adjacent) = usedAdjacentSpace;
usedSpace.get(Opposite) = usedOppositeSpace;
return usedSpace;
}
}//Shaping
}//IVD
*/
#endif // LINE_H

View File

@ -0,0 +1,203 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "cairocanvas.h"
#include <functional>
#include <iostream>
#include "assert.h"
namespace IVD
{
cairo_surface_t* createNewCairoSurfaceForPlatform(const Dimens size)
{ return cairo_image_surface_create(CAIRO_FORMAT_RGB24, size.w, size.h); }
void CairoCanvas::clip()
{
for(Rect c : clips)
{
cairo_rectangle(myCai, c.c.x, c.c.y, c.d.w, c.d.h);
cairo_clip(myCai);
}
}
void CairoCanvas::drawWith(Color theColor,
Color::AlphaType alpha,
std::function<void(cairo_t*)> fun)
{
cairo_save(myCai);
clip();
cairo_move_to(myCai, offset.x, offset.y);
cairo_set_source_rgba(myCai,
(theColor.red + 1) / 256.0,
(theColor.green + 1) / 256.0,
(theColor.blue + 1) / 256.0,
(alpha + 1) / 256.0);
fun(myCai);
cairo_restore(myCai);
}
void CairoCanvas::setSize(const Dimens size)
{
if(myCai) freeContext();
surface = createNewCairoSurfaceForPlatform(size);
myCai = cairo_create(surface);
}
Dimens CairoCanvas::getSize()
{
Dimens size;
if(!myCai) return size;
size.w = cairo_image_surface_get_width(surface);
size.h = cairo_image_surface_get_height(surface);
return size;
}
void CairoCanvas::clear()
{
//Magic numbers because it's just fucking white, people.
drawWith(Color(255,255,255), 255, [&](cairo_t* cai)
{
cairo_paint(cai);
});
}
void CairoCanvas::fillRect(Rect r, Color theColor)
{
drawWith(theColor, alpha, [&](cairo_t* cai)
{
cairo_rectangle(cai, r.c.x, r.c.y, r.d.w, r.d.h); //Alphabet soup
cairo_fill(cai);
});
}
void CairoCanvas::strokeRect(Rect r, int size, Color theColor, Color::AlphaType alpha)
{
drawWith(theColor, alpha, [&](cairo_t* cai)
{
cairo_set_line_width(cai, size);
cairo_rectangle(cai, r.c.x - .5, r.c.y - .5, r.d.w, r.d.h);
cairo_stroke(cai);
});
}
void CairoCanvas::drawLine(Coords start, Coords end, int size, Color theColor, Color::AlphaType alpha)
{
}
void CairoCanvas::drawGradient(Rect box,
Color toftColor, Color::AlphaType toftAlpha,
Color boriColor, Color::AlphaType boriAlpha, Angle theAngle)
{
assert(false);
}
void CairoCanvas::drawDropShadow(Rect box, int size, Color theColor, Color::AlphaType )
{
assert(false);
}
void CairoCanvas::drawBitmapRGBoptionalA(Coords dest, int imageStride, int width, int height, int channels, unsigned char* data)
{
//Goddddddddd we have to rearrange the pixel data ughh...
const auto format = CAIRO_FORMAT_ARGB32;
#ifdef BIG_ENDIAN_SYSTEM
const int destinationAlphaOffset = 0;
const int destinationRedOffset = 1;
const int destinationGreenOffset = 2;
const int destinationBlueOffset = 3;
#elif LITTLE_ENDIAN_SYSTEM
const int destinationAlphaOffset = 3;
const int destinationRedOffset = 2;
const int destinationGreenOffset = 1;
const int destinationBlueOffset = 0;
#endif
const int stride = cairo_format_stride_for_width(format, width);
const int dstPixelWidth = 4;
const int srcPixelWidth = channels;
std::vector<unsigned char> theTrueNameOfBaal(stride * height * channels);
for(int row = 0; row != height; ++row)
{
unsigned char* src = data + width * channels * row;
unsigned char* dst = &theTrueNameOfBaal[0] + stride * row;
int di = 0;
int si = 0;
while(si != width * channels)
{
//Pixel, *not* subpixel!
unsigned char alpha = dst[di + destinationAlphaOffset] = channels == 4 ? src[si + 3]
: 0xff;
dst[di + destinationRedOffset] = src[si + 0] * -alpha;
dst[di + destinationGreenOffset] = src[si + 1] * -alpha;
dst[di + destinationBlueOffset] = src[si + 2] * -alpha;
//I guess that's how you do premultiplied alpha...
di += dstPixelWidth;
si += srcPixelWidth;
}
}
cairo_surface_t* mySurf = cairo_image_surface_create_for_data(&theTrueNameOfBaal[0],
format,
width,
height,
stride);
cairo_save(myCai);
clip();
//Does this need to be offset by .5?
Coords trueDest = dest + offset;
cairo_rectangle(myCai, trueDest.x, trueDest.y, width, height);
cairo_set_source_surface(myCai, mySurf, trueDest.x, trueDest.y);
cairo_fill(myCai);
cairo_restore(myCai);
cairo_surface_destroy(mySurf);
}
void CairoCanvas::flush()
{
cairo_surface_flush(surface);
}
bool CairoCanvas::ready()
{
if(myCai) return true;
assert(!surface);
return false;
}
Bitmap CairoCanvas::getBitmap()
{
Bitmap bitmap;
bitmap.stride = cairo_image_surface_get_stride(surface);
bitmap.width = cairo_image_surface_get_width(surface);
bitmap.height = cairo_image_surface_get_height(surface);
bitmap.data = cairo_image_surface_get_data(surface);
bitmap.channels = 3; //rgb24 see above
return bitmap;
}
}//IVD

View File

@ -0,0 +1,72 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef CAIROCANVAS_H
#define CAIROCANVAS_H
#include <functional>
#include "canvas.h"
#include "cairo/cairo.h"
namespace IVD
{
cairo_surface_t* createNewCairoSurfaceForPlatform(const Dimens size);
class CairoCanvas : public Canvas
{
cairo_t* myCai;
cairo_surface_t* surface;
void freeContext()
{
cairo_surface_destroy(surface);
cairo_destroy(myCai);
surface = nullptr;
myCai = nullptr;
}
void clip();
void drawWith(Color theColor, Color::AlphaType alpha, std::function<void(cairo_t*)> fun);
public:
CairoCanvas(): myCai(nullptr), surface(nullptr) {}
~CairoCanvas() { freeContext(); }
void setSize(const Dimens size) final;
Dimens getSize() final;
bool ready();
void clear();
void fillRect(Rect r, Color theColor) final;
void strokeRect(Rect r, int size, Color theColor, Color::AlphaType alpha) final;
void drawLine(Coords start, Coords end, int size, Color theColor, Color::AlphaType alpha) final;
void drawGradient(Rect box,
Color toftColor,
Color::AlphaType toftAlpha,
Color boriColor,
Color::AlphaType boriAlpha,
Angle theAngle) final;
void drawDropShadow(Rect box, int size, Color theColor, Color::AlphaType alpha) final;
virtual void drawBitmapRGBoptionalA(Coords dest,
int stride,
int width,
int height,
int channels,
unsigned char* data) final;
//Defined in */TextDriver.cpp
void drawText(Coords origin, const std::string text, DisplayItem* style) final;
void flush() final;
Bitmap getBitmap() final;
};
}//IVD
#endif // CAIROCANVAS_H

View File

@ -0,0 +1,683 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "specific_driver_sdl/sdldriver.h"
#include "states.h"
#include "defaults.h"
#include "statemanager.h"
#include <reprodyne.h>
namespace IVD
{
SDLdriver::SDLdriver():
rootWithMouseFocus(nullptr),
hoverInvalidated(true)
{
int code = SDL_Init(SDL_INIT_VIDEO);
assert(code >= 0);
reprodyne_open_scope(this);
}
SDLdriver::~SDLdriver()
{
SDL_Quit();
}
Driver* createDefaultDriver()
{ return new SDLdriver; }
void SDLdriver::invalidateHover(const Coords point, const Uint32 windowID)
{
hoverInvalidated = true;
DisplayItem* myRoot = nullptr;
for(const auto& pair : pairs)
{
if(!pair.second.window) continue;
if(pair.second.window->getWindowId() == windowID)
{
myRoot = pair.first;
break;
}
}
getStateManager()->mutateAll(States::Window::MouseFocus, false);
if(myRoot)
getStateManager()->mutateIfObserved(StateKey(States::Window::MouseFocus, myRoot), true);
rootWithMouseFocus = myRoot;
}
void SDLdriver::invalidateHoverDesperately()
{
Coords makeShiftMousePosition;
SDL_GetMouseState(&makeShiftMousePosition.x, &makeShiftMousePosition.y);
makeShiftMousePosition.x = reprodyne_intercept_double(this, "makeshift-mouse-x", makeShiftMousePosition.x);
makeShiftMousePosition.y = reprodyne_intercept_double(this, "makeshift-mouse-y", makeShiftMousePosition.y);
const auto windowId = reprodyne_intercept_double(this,
"makeshift-mouse-position-window-id",
SDL_GetWindowID(SDL_GetMouseFocus()));
invalidateHover(makeShiftMousePosition, windowId);
}
void SDLdriver::addDisplayItem(DisplayItem *item)
{
auto& pair = pairs[item];
if(!pair.window)
{
pair.window = std::make_unique<SDLwindow>(item);
getStateManager()->setTriggerIfObserved(StateKey(States::Window::Initialized, item));
}
pair.destroy = false;
}
void SDLdriver::removeDisplayItem(DisplayItem* item)
{
auto it = pairs.find(item);
if(it == pairs.end()) return;
it->second.destroy = true;
}
void SDLdriver::processEvents()
{
StateManager* stateManager = getStateManager();
auto mapSDLscanCodeToStateSymbol = [&](const Uint16 scanCode) -> std::optional<States::Symbol>
{
std::optional<States::Symbol> result;
switch(scanCode)
{
case SDL_SCANCODE_AUDIOPLAY: return States::Key::Play;
case SDL_SCANCODE_AUDIOSTOP: return States::Key::Stop;
case SDL_SCANCODE_AUDIONEXT: return States::Key::Next;
case SDL_SCANCODE_AUDIOPREV: return States::Key::Previous;
case SDL_SCANCODE_LGUI: return States::Key::LeftSuper;
case SDL_SCANCODE_LSHIFT: return States::Key::LeftShift;
case SDL_SCANCODE_LCTRL: return States::Key::LeftCtrl;
case SDL_SCANCODE_LALT: return States::Key::LeftAlt;
case SDL_SCANCODE_RGUI: return States::Key::RightSuper;
case SDL_SCANCODE_RSHIFT: return States::Key::RightShift;
case SDL_SCANCODE_RCTRL: return States::Key::RightCtrl;
case SDL_SCANCODE_RALT: return States::Key::RightAlt;
case SDL_SCANCODE_CAPSLOCK: return States::Key::CapsLock;
//Alt G
case SDL_SCANCODE_MENU: return States::Key::Menu;
case SDL_SCANCODE_PRINTSCREEN: return States::Key::PrintScreen;
case SDL_SCANCODE_SCROLLLOCK: return States::Key::ScrollLock;
case SDL_SCANCODE_PAUSE: return States::Key::Pause;
case SDL_SCANCODE_INSERT: return States::Key::Insert;
case SDL_SCANCODE_DELETE: return States::Key::Delete;
case SDL_SCANCODE_HOME: return States::Key::Home;
case SDL_SCANCODE_END: return States::Key::End;
case SDL_SCANCODE_PAGEUP: return States::Key::PageUp;
case SDL_SCANCODE_PAGEDOWN: return States::Key::PageDown;
case SDL_SCANCODE_LEFT: return States::Key::LeftArrow;
case SDL_SCANCODE_RIGHT: return States::Key::RightArrow;
case SDL_SCANCODE_UP: return States::Key::UpArrow;
case SDL_SCANCODE_DOWN: return States::Key::DownArrow;
case SDL_SCANCODE_NUMLOCKCLEAR: return States::Key::NumLock;
case SDL_SCANCODE_ESCAPE: return States::Key::Escape; //Must be Italian
case SDL_SCANCODE_F1: return States::Key::F1;
case SDL_SCANCODE_F2: return States::Key::F2;
case SDL_SCANCODE_F3: return States::Key::F3;
case SDL_SCANCODE_F4: return States::Key::F4;
case SDL_SCANCODE_F5: return States::Key::F5;
case SDL_SCANCODE_F6: return States::Key::F6;
case SDL_SCANCODE_F7: return States::Key::F7;
case SDL_SCANCODE_F8: return States::Key::F8;
case SDL_SCANCODE_F9: return States::Key::F9;
case SDL_SCANCODE_F10: return States::Key::F10;
case SDL_SCANCODE_F11: return States::Key::F11;
case SDL_SCANCODE_F12: return States::Key::F12;
case SDL_SCANCODE_GRAVE: return States::Key::Tilde;
case SDL_SCANCODE_1: return States::Key::RowNum1;
case SDL_SCANCODE_2: return States::Key::RowNum2;
case SDL_SCANCODE_3: return States::Key::RowNum3;
case SDL_SCANCODE_4: return States::Key::RowNum4;
case SDL_SCANCODE_5: return States::Key::RowNum5;
case SDL_SCANCODE_6: return States::Key::RowNum6;
case SDL_SCANCODE_7: return States::Key::RowNum7;
case SDL_SCANCODE_8: return States::Key::RowNum8;
case SDL_SCANCODE_9: return States::Key::RowNum9;
case SDL_SCANCODE_0: return States::Key::RowNum0;
case SDL_SCANCODE_MINUS: return States::Key::Minus;
case SDL_SCANCODE_EQUALS: return States::Key::Equals;
case SDL_SCANCODE_BACKSPACE: return States::Key::Backspace;
case SDL_SCANCODE_TAB: return States::Key::Tab;
case SDL_SCANCODE_Q: return States::Key::Q;
case SDL_SCANCODE_W: return States::Key::W;
case SDL_SCANCODE_E: return States::Key::E;
case SDL_SCANCODE_R: return States::Key::R;
case SDL_SCANCODE_T: return States::Key::T;
case SDL_SCANCODE_Y: return States::Key::Y;
case SDL_SCANCODE_U: return States::Key::U;
case SDL_SCANCODE_I: return States::Key::I;
case SDL_SCANCODE_O: return States::Key::O;
case SDL_SCANCODE_P: return States::Key::P;
case SDL_SCANCODE_LEFTBRACKET: return States::Key::LeftBracket;
case SDL_SCANCODE_RIGHTBRACKET: return States::Key::RightBracket;
case SDL_SCANCODE_BACKSLASH: return States::Key::Backslash;
case SDL_SCANCODE_A: return States::Key::A;
case SDL_SCANCODE_S: return States::Key::S;
case SDL_SCANCODE_D: return States::Key::D;
case SDL_SCANCODE_F: return States::Key::F;
case SDL_SCANCODE_G: return States::Key::G;
case SDL_SCANCODE_H: return States::Key::H;
case SDL_SCANCODE_J: return States::Key::J;
case SDL_SCANCODE_K: return States::Key::K;
case SDL_SCANCODE_L: return States::Key::L;
case SDL_SCANCODE_SEMICOLON: return States::Key::Semicolon;
case SDL_SCANCODE_APOSTROPHE: return States::Key::Apostrophe;
case SDL_SCANCODE_RETURN: return States::Key::Return;
case SDL_SCANCODE_Z: return States::Key::Z;
case SDL_SCANCODE_X: return States::Key::X;
case SDL_SCANCODE_C: return States::Key::C;
case SDL_SCANCODE_V: return States::Key::V;
case SDL_SCANCODE_B: return States::Key::B;
case SDL_SCANCODE_N: return States::Key::N;
case SDL_SCANCODE_M: return States::Key::M;
case SDL_SCANCODE_COMMA: return States::Key::Comma;
case SDL_SCANCODE_PERIOD: return States::Key::Period;
case SDL_SCANCODE_SLASH: return States::Key::Slash;
case SDL_SCANCODE_SPACE: return States::Key::Spacebar;
case SDL_SCANCODE_KP_DIVIDE: return States::Key::Pad::Slash;
case SDL_SCANCODE_KP_MULTIPLY: return States::Key::Pad::Star;
case SDL_SCANCODE_KP_MINUS: return States::Key::Pad::Minus;
case SDL_SCANCODE_KP_7: return States::Key::Pad::Num7;
case SDL_SCANCODE_KP_8: return States::Key::Pad::Num8;
case SDL_SCANCODE_KP_9: return States::Key::Pad::Num9;
case SDL_SCANCODE_KP_PLUS: return States::Key::Pad::Plus;
case SDL_SCANCODE_KP_4: return States::Key::Pad::Num4;
case SDL_SCANCODE_KP_5: return States::Key::Pad::Num5;
case SDL_SCANCODE_KP_6: return States::Key::Pad::Num6;
case SDL_SCANCODE_KP_TAB: return States::Key::Pad::Tab;
case SDL_SCANCODE_KP_1: return States::Key::Pad::Num1;
case SDL_SCANCODE_KP_2: return States::Key::Pad::Num2;
case SDL_SCANCODE_KP_3: return States::Key::Pad::Num3;
case SDL_SCANCODE_KP_0: return States::Key::Pad::Num0;
case SDL_SCANCODE_KP_PERIOD: return States::Key::Pad::Period;
case SDL_SCANCODE_KP_ENTER: return States::Key::Pad::Return;
default: return result;
}
};
auto mapSDLmouseButtonToSymbol = [&](const Uint8 button) -> States::Symbol
{
switch(button)
{
case SDL_BUTTON_LEFT: return States::Mouse::ButtonLeft;
case SDL_BUTTON_MIDDLE: return States::Mouse::ButtonMiddle;
case SDL_BUTTON_RIGHT: return States::Mouse::ButtonRight;
case SDL_BUTTON_X1: return States::Mouse::ButtonFour;
case SDL_BUTTON_X2: return States::Mouse::ButtonFive;
default: assert(false);
}
};
//TODO: Would it be faster to do a search over the states and then see if it matches
// the event, rather than search and see if the state is observed?
SDL_Event event;
if(reprodyne_intercept_double(this, "poll-event", SDL_PollEvent(&event)))
{
const double eventType = reprodyne_intercept_double(this, "event-type", event.type);
switch(int(eventType))
{
case SDL_KEYDOWN:
case SDL_KEYUP:
{
const auto scancode = reprodyne_intercept_double(this, "scancode", event.key.keysym.scancode);
const auto symbol = mapSDLscanCodeToStateSymbol(scancode);
if(symbol)
{
if(eventType == SDL_KEYDOWN)
stateManager->setTriggerIfObserved(States::getButtonPress(*symbol));
else if(eventType == SDL_KEYUP)
stateManager->setTriggerIfObserved(States::getButtonRelease(*symbol));
}
break;
}
case SDL_MOUSEBUTTONDOWN:
{
const auto keysym = reprodyne_intercept_double(this, "mouse-button-down", event.button.button);
States::Symbol sym = mapSDLmouseButtonToSymbol(keysym);
stateManager->setTriggerIfObserved(States::getButtonPress(sym));
break;
}
case SDL_MOUSEBUTTONUP: //Bucko
{
const auto keysym = reprodyne_intercept_double(this, "mouse-button-up", event.button.button);
States::Symbol sym = mapSDLmouseButtonToSymbol(keysym);
stateManager->setTriggerIfObserved(States::getButtonRelease(sym));
break;
}
case SDL_MOUSEMOTION:
{
stateManager->setTriggerIfObserved(States::Mouse::Motion);
mousePointWindow.x = reprodyne_intercept_double(this, "mouse-motion-x", event.motion.x);
mousePointWindow.y = reprodyne_intercept_double(this, "mouse-motion-y", event.motion.y);
const auto windowId = reprodyne_intercept_double(this, "motion-window-id", event.motion.windowID);
invalidateHover(Coords(mousePointWindow.x, mousePointWindow.y), windowId);
break;
}
case SDL_MOUSEWHEEL:
{
//Doesn't necessarily invalidate hover
stateManager->setTriggerIfObserved(States::Mouse::Wheel);
if(event.wheel.direction == SDL_MOUSEWHEEL_NORMAL)
{
scrollDist.x = event.wheel.x;
scrollDist.y = event.wheel.y;
}
else if(event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
{
scrollDist.x = event.wheel.x * -1;
scrollDist.y = event.wheel.y * -1;
}
//All that matters as far as the harness is concerned, is what the result is
// we'll get to these two lines in either mode.
scrollDist.x = reprodyne_intercept_double(this, "scroll-dist-x", scrollDist.x);
scrollDist.y = reprodyne_intercept_double(this, "scroll-dist-y", scrollDist.y);
break;
}
case SDL_WINDOWEVENT:
{
for(const auto& pair : pairs)
{
if(!pair.second.window)
continue;
DisplayItem* item = pair.first;
const auto currentRootWindowId = pair.second.window->getWindowId();
const auto eventWindowId = reprodyne_intercept_double(this,
"window-event-window-id",
event.window.windowID);
if(currentRootWindowId != eventWindowId) continue;
switch(int(reprodyne_intercept_double(this, "window-event", event.window.event)))
{
case SDL_WINDOWEVENT_SIZE_CHANGED:
{
//If in top down mode
if(Default::Filter::getWindowSizeStrategy(item) == Property::TopDown)
invalidateGeometry(item);
//If the canvas is updated too soon after the window is resized, then the full
// area isn't drawn and there will be a black area (if larger than before)
// once the window system responds to the resize request. But we still get a
// resize event, so we push another redraw.
invalidateCanvas(item);
break;
}
case SDL_WINDOWEVENT_MOVED:
invalidatePosition(item);
break;
case SDL_WINDOWEVENT_CLOSE:
stateManager->setTriggerIfObserved(StateKey(States::Window::CloseRequest, item));
break;
case SDL_WINDOWEVENT_LEAVE:
case SDL_WINDOWEVENT_ENTER:
invalidateHoverDesperately();
break;
case SDL_WINDOWEVENT_SHOWN:
invalidateCanvas(item);
stateManager->setTriggerIfObserved(StateKey(States::Window::Shown, item));
break;
}
}
break;
}
case SDL_QUIT:
{
stateManager->setTriggerIfObserved(States::App::CloseApp);
break;
}
}
}
//Button states are out of sync with events by definition.
{
int length;
const Uint8* keyStates = SDL_GetKeyboardState(&length);
for(Uint16 i = 0; i != length; ++i)
{
auto symbol = mapSDLscanCodeToStateSymbol(i);
if(!symbol) continue;
const StateKey theStateKey(States::getButtonActive(*symbol));
const bool pressed = reprodyne_intercept_double(this, "key-state", keyStates[i]);
stateManager->mutateIfObserved(theStateKey, pressed);
}
}
{
const Uint32 mask = reprodyne_intercept_double(this,
"mouse-mask",
SDL_GetMouseState(&queriedMousePoint.x,
&queriedMousePoint.y));
const StateKey left(States::getButtonActive(States::Mouse::ButtonLeft));
const StateKey middle(States::getButtonActive(States::Mouse::ButtonMiddle));
const StateKey right(States::getButtonActive(States::Mouse::ButtonRight));
const StateKey four(States::getButtonActive(States::Mouse::ButtonFour));
const StateKey five(States::getButtonActive(States::Mouse::ButtonFive));
auto thingamajigulator = [&](const auto code, const StateKey stateKey)
{
if((mask & code) == code)
stateManager->mutateIfObserved(stateKey, true);
else
stateManager->mutateIfObserved(stateKey, false);
};
thingamajigulator(SDL_BUTTON_LMASK, left);
thingamajigulator(SDL_BUTTON_MMASK, middle);
thingamajigulator(SDL_BUTTON_RMASK, right);
thingamajigulator(SDL_BUTTON_X1MASK, four);
thingamajigulator(SDL_BUTTON_X2MASK, five);
}
}
void SDLdriver::invalidateGeometry(DisplayItem *item)
{
if(pairs.count(item->getRoot()))
pairs.at(item->getRoot()).invalidGeometry = true;
}
void SDLdriver::invalidatePosition(DisplayItem* item)
{
auto root = item->getRoot();
if(root == item)
{
pairs.at(root).invalidPosition = true;
}
else invalidateGeometry(item);
}
void SDLdriver::invalidateCanvas(DisplayItem *item)
{
if(pairs.count(item->getRoot()))
pairs.at(item->getRoot()).invalidCanvas = true;
}
void SDLdriver::invalidateTitleText(DisplayItem *item)
{
auto it = pairs.find(item);
if(it == pairs.end()) return;
it->second.invalidTitleText = true;
}
void SDLdriver::invalidateVisibility(DisplayItem *item)
{
if(item->getRoot() == item)
pairs[item].invalidVisibility = true;
else
invalidateCanvas(item->getRoot());
}
void SDLdriver::refresh()
{
hoverInvalidated = false;
//That kinda thing.
for(auto it = pairs.begin(); it != pairs.end(); /*nope*/)
{
DisplayItem* item = it->first;
auto& pairData = it->second;
if(pairData.destroy)
pairData.window.reset();
if(!pairData.window)
{
it = pairs.erase(it);
continue;
}
SDLwindow* window = pairData.window.get();
const int displayIndex = reprodyne_intercept_double(this, "display-index", window->getDisplayIndex());
SDL_Rect displayUsable;
SDL_Rect displayFull;
SDL_GetDisplayBounds(displayIndex, &displayFull);
SDL_GetDisplayUsableBounds(displayIndex, &displayUsable);
displayUsable.w = reprodyne_intercept_double(this, "display-usable-w", displayUsable.w);
displayUsable.h = reprodyne_intercept_double(this, "display-usable-h", displayUsable.h);
displayUsable.x = reprodyne_intercept_double(this, "display-usable-x", displayUsable.x);
displayUsable.y = reprodyne_intercept_double(this, "display-usable-y", displayUsable.y);
displayFull.w = reprodyne_intercept_double(this, "display-full-w", displayFull.w);
displayFull.h = reprodyne_intercept_double(this, "display-full-h", displayFull.h);
displayFull.x = reprodyne_intercept_double(this, "display-full-x", displayFull.x);
displayFull.y = reprodyne_intercept_double(this, "display-full-y", displayFull.y);
if(pairData.invalidGeometry)
{
//We just assume any time geometry is invalidated, resizability is as well.
pairData.window->setResizable(Default::Filter::checkResizable(item));
const bool fullscreen = Default::Filter::getFullscreen(item);
GeometryProposal prop;
//This isn't a great way to handle this. How do we set fullscreen?
//How do we know what the true bounds are? (There is a "fake" fullscreen mode)
if(fullscreen)
{
prop.proposedDimensions.get(Angle::Horizontal) = displayFull.w;
prop.proposedDimensions.get(Angle::Vertical) = displayFull.h;
//The default is all false for the expand/shrink flags
}
else
{
//if in top-down mode, then get the window size and lock both
// dimensions. Else, in order, use window-size-hint, then
// starting size. Normal size expressions will lock the
// viewport in the material, we don't need them here.
prop.proposedDimensions.get(Angle::Adjacent) = displayUsable.w;
prop.proposedDimensions.get(Angle::Opposite) = displayUsable.h;
const int sizeStrategy = Default::Filter::getWindowSizeStrategy(item);
if(sizeStrategy == Property::BottomUp)
{
prop.shrinkForAngle(Angle::Vertical) = true;
prop.shrinkForAngle(Angle::Horizontal) = true;
}
else
{
//Use the current window size if initial not set. Relying on the
// user to make sure there are initial sizes on first pass...
const Dimens currentSize = window->getClientArea();
prop.proposedDimensions.get(Angle::Adjacent) = currentSize.get(Angle::Adjacent);
prop.proposedDimensions.get(Angle::Opposite) = currentSize.get(Angle::Opposite);
}
}
item->shape(prop);
item->computerAbsoluteOffsets(Coords());
invalidateHoverDesperately();
const Dimens drawableDimens = item->getViewportDimens();
if(!pairData.previousSizeSet || pairData.previousDimens != drawableDimens)
{
window->setClientArea(drawableDimens);
pairData.previousDimens = drawableDimens;
pairData.previousSizeSet = true;
//Rely on window size change event invalidating the canvas again, otherwise the update
// is too soon and the window system hasn't responded to the resize request yet.
}
else pairData.invalidCanvas = true;
//Does this make it so that the surface doesn't "jiggle" by one pixel on resizes?
pairData.invalidCanvas = true;
pairData.invalidPosition = true;
pairData.invalidGeometry = false;
}
if(pairData.invalidPosition)
{
//We don't use SDL_WINDOWPOS_CENTERED because we can't compare
// it to the current position of the window for flag invalidation.
//Looking at the SDL source code, it doesn't make any special calls or
// anything, it just does a basic centering equation.
//Also, it uses DisplayBounds and not UsableDisplayBound like I would
// like. So...
//ALSO: Nothing fancy for different screens, etc. TODO
//
//This doesn't really work, because if it's still grabbed, then the
// resize doesn't happen, and an event is not generated. And nothing is
// generated once the mouse is released... Shit. It only really works
// with constant polling.
/*
if(window->getWindowPos() == theNewPosition)
{
invalidateHoverDesperately(); //AFTER we move the window (shouldn't matter tho?)
pairData.invalidPosition = false;
}
*/
//else, we couldn't set the position. Probably because the window is still grabbed.
// In this case we just spin until the user lets go. Hopefully no other position puts us
// in this state, such as attempting to set the position whilst fullscreen.
//(still doesn't work TODO)
}
if(pairData.invalidCanvas)
{
if(window->getCanvas()->ready())
{
auto canvas = window->getCanvas();
canvas->clear();
currentCanvas = canvas;
item->render();
window->present();
}
pairData.invalidCanvas = false;
}
if(pairData.invalidTitleText)
{
window->setTitleText(Default::Filter::getTitleText(item));
pairData.invalidTitleText = false;
}
if(pairData.invalidResizability)
{
window->setResizable(Default::Filter::checkResizable(item));
pairData.invalidResizability = false;
}
if(pairData.invalidVisibility)
{
window->setVisible(Default::Filter::getVisibility(item));
pairData.invalidVisibility = false;
}
{
const Bitmap bipbap = window->getCanvas()->getBitmap();
reprodyne_validate_bitmap_hash(item, "bitmap",
bipbap.width * bipbap.channels,
bipbap.height,
bipbap.stride,
bipbap.data);
}
++it;
}
}
bool SDLdriver::checkAnythingToDo()
{
if(hoverInvalidated)
return true;
if(reprodyne_intercept_double(this, "has-event", SDL_HasEvents(SDL_FIRSTEVENT, SDL_LASTEVENT)))
return true;
for(auto& pair : pairs)
{
auto& data = pair.second;
//Is data.destroy ever true here? This should get run right after refresh which handles that...
// Of course it handles everything else, too... Except invalidPosition, sometimes.
if(data.destroy || data.invalidCanvas || data.invalidGeometry || data.invalidPosition ||
data.invalidTitleText || data.invalidVisibility || data.invalidResizability)
return true;
}
return false;
}
}//IVD

View File

@ -0,0 +1,97 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef SDLDRIVER_H
#define SDLDRIVER_H
#include "driver.h"
#include "specific_driver_sdl/sdlwindow.h"
#include <map>
namespace IVD
{
class SDLdriver : public Driver
{
struct ItemWindowPair
{
std::unique_ptr<SDLwindow> window;
bool destroy;
bool invalidGeometry;
bool invalidPosition;
bool invalidCanvas;
bool invalidTitleText;
bool invalidResizability;
bool invalidVisibility;
//Fullscreen....... too tired do it later
//Not using std::optional here because for some reason it doesn't
// copy the structure properly...
bool previousSizeSet;
Dimens previousDimens;
ItemWindowPair():
destroy(false),
invalidGeometry(true),
invalidPosition(true),
invalidCanvas(true),
invalidTitleText(true),
invalidResizability(true),
invalidVisibility(true),
previousSizeSet(false)
{}
};
std::map<DisplayItem*, ItemWindowPair> pairs;
DisplayItem* rootWithMouseFocus;
//Coords mouseHoverPoint;
Coords mousePointWindow;
Coords scrollDist;
Coords queriedMousePoint;
Canvas* currentCanvas;
bool hoverInvalidated;
void invalidateHover(const Coords point, const Uint32 windowID);
void invalidateHoverDesperately();
public:
SDLdriver();
~SDLdriver();
void addDisplayItem(DisplayItem* item);
void removeDisplayItem(DisplayItem* item);
void processEvents();
void invalidateGeometry(DisplayItem* item);
void invalidatePosition(DisplayItem* item);
void invalidateCanvas(DisplayItem* item);
void invalidateTitleText(DisplayItem* item);
void invalidateVisibility(DisplayItem* item);
void refresh();
bool checkAnythingToDo();
bool checkHoverInvalidated()
{ return hoverInvalidated; }
Canvas* getCanvas()
{ return currentCanvas; }
DisplayItem* getWindowItemWithMouseFocus()
{ return rootWithMouseFocus; }
Coords getMousePointRelativeToWindow()
{ return mousePointWindow; }
};
}//IVD
#endif // SDLDRIVER_H

View File

@ -0,0 +1,221 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "sdlwindow.h"
#include "defaults.h"
static IVD::Dimens getDrawableAreaForRenderer(SDL_Renderer* theRenderer)
{
IVD::Dimens drawable;
SDL_GetRendererOutputSize(theRenderer, &drawable.w, &drawable.h);
return drawable;
}
void SDLwindow::createWindow(const std::string title, const IVD::Rect theRect, const int flags)
{
myWindow = SDL_CreateWindow(title.c_str(), theRect.c.x, theRect.c.y, theRect.d.w, theRect.d.h, flags);
assert(myWindow);
myRenderer = SDL_CreateRenderer(myWindow, -1, SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC);
assert(myRenderer);
}
void SDLwindow::destroyWindow()
{
SDL_DestroyRenderer(myRenderer);
SDL_DestroyWindow(myWindow);
myRenderer = nullptr;
myWindow = nullptr;
}
SDLwindow::SDLwindow(IVD::DisplayItem* item):
myFormat(SDL_AllocFormat(SDL_PIXELFORMAT_RGB24))
{
int flags = SDL_WINDOW_HIDDEN;
if(IVD::Default::Filter::checkResizable(item))
flags |= SDL_WINDOW_RESIZABLE;
createWindow("", IVD::Rect(), flags);
}
SDLwindow::~SDLwindow()
{
SDL_FreeFormat(myFormat);
destroyWindow();
//bye bye :<
}
IVD::Dimens SDLwindow::getClientArea()
{
IVD::Dimens myDimens;
SDL_GetWindowSize(myWindow, &myDimens.w, &myDimens.h);
return myDimens;
}
IVD::Dimens SDLwindow::getWindowArea()
{
int top;
int left;
int bottom;
int right;
SDL_GetWindowBordersSize(myWindow, &top, &left, &bottom, &right);
IVD::Dimens myDimens = getClientArea();
myDimens.h += top + bottom;
myDimens.w += left + right;
return myDimens;
}
IVD::Coords SDLwindow::getWindowPos()
{
IVD::Coords myCoords;
SDL_GetWindowPosition(myWindow, &myCoords.x, &myCoords.y);
return myCoords;
}
void SDLwindow::setClientArea(const IVD::Dimens size)
{
{
Uint32 flags = SDL_GetWindowFlags(myWindow);
if((flags & SDL_WINDOW_FULLSCREEN) != SDL_WINDOW_FULLSCREEN &&
(flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)
{
SDL_SetWindowSize(myWindow, size.w, size.h);
}
}
myCanvas.setSize(size);
}
void SDLwindow::setWindowPos(const IVD::Coords pos)
{
//uh.. What about decorations?
//Well. According to https://bugzilla.libsdl.org/show_bug.cgi?id=3845
//These SDL_*WindowPosition functions return the position of the viewport, and
// not the window decorations. This is a bug in SDL. Ideally, this
// next call would correct for the decoration offset. TODO
//coordinateOffset = pos + SDL_GetWindowBordersSize() values.
SDL_SetWindowPosition(myWindow, pos.x, pos.y);
}
void SDLwindow::setResizable(const bool flag)
{
int flags = SDL_GetWindowFlags(myWindow);
if(flag != ((flags & SDL_WINDOW_RESIZABLE) == SDL_WINDOW_RESIZABLE))
{
if(!flag) flags &= ~SDL_WINDOW_RESIZABLE;
else flags |= SDL_WINDOW_RESIZABLE;
assert(flag == ((flags & SDL_WINDOW_RESIZABLE) == SDL_WINDOW_RESIZABLE));
IVD::Rect windowRect;
windowRect.d = getClientArea();
windowRect.c = getWindowPos();
const std::string title = SDL_GetWindowTitle(myWindow);
destroyWindow();
createWindow(title, windowRect, flags);
setClientArea(windowRect.d);
}
}
void SDLwindow::setVisible(const bool flag)
{
if(flag)
SDL_ShowWindow(myWindow);
else
SDL_HideWindow(myWindow);
}
void SDLwindow::setTitleText(const std::string title)
{
SDL_SetWindowTitle(myWindow, title.c_str());
}
void SDLwindow::present()
{
IVD::Dimens myDimens = getDrawableAreaForRenderer(myRenderer);
//Crashes if less than 2 because... Pixel offsets? Sorry too busy to figure out.
if(myDimens.w < 2 || myDimens.h < 2) return;
const int height = myDimens.h;
const int widthInPixels = myDimens.w;
void* destData;
int destPitch;
SDL_Texture* myTarget = SDL_CreateTexture(myRenderer,
myFormat->format,
SDL_TEXTUREACCESS_STREAMING,
myDimens.w,
myDimens.h);
SDL_LockTexture(myTarget, nullptr, &destData, &destPitch);
myCanvas.flush();
IVD::Bitmap mySurface = myCanvas.getBitmap();
unsigned char* sourceData = mySurface.data;
const int sourcePitch = mySurface.stride;
auto mapMaskToOffset = [](const Uint32 mask)
{
if(mask == 0x000000ff) return 0;
else if(mask == 0x0000ff00) return 1;
else if(mask == 0x00ff0000) return 2;
else if(mask == 0xff000000) return 3;
assert(false);
};
const int destRedOffset = mapMaskToOffset(myFormat->Rmask);
const int destGreenOffset = mapMaskToOffset(myFormat->Gmask);
const int destBlueOffset = mapMaskToOffset(myFormat->Bmask);
const int pixelWidthSDL = myFormat->BytesPerPixel;
//As per the documentation for CAIRO_FORMAT_RGB24
const int sourceRedOffset = 2;
const int sourceGreenOffset = 1;
const int sourceBlueOffset = 0;
const int pixelWidthCairo = 4;
const int destDataWidth = widthInPixels * pixelWidthSDL;
const int sourceDataWidth = widthInPixels * pixelWidthCairo;
for(int row = 0; row != height; ++row)
{
unsigned char* dest = (unsigned char*)destData + (destPitch * row);
unsigned char* source = sourceData + (sourcePitch * row);
int di = 0;
int si = 0;
while(di != destDataWidth && si != sourceDataWidth)
{
dest[di + destRedOffset] = source[si + sourceRedOffset];
dest[di + destGreenOffset] = source[si + sourceGreenOffset];
dest[di + destBlueOffset] = source[si + sourceBlueOffset];
di += pixelWidthSDL;
si += pixelWidthCairo;
}
}
SDL_UnlockTexture(myTarget);
SDL_RenderCopy(myRenderer, myTarget, nullptr, nullptr);
SDL_DestroyTexture(myTarget);
SDL_RenderPresent(myRenderer);
}

View File

@ -0,0 +1,52 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef SDLWINDOW_H
#define SDLWINDOW_H
#include "SDL2/SDL.h"
#include "geometry.h"
#include "specific_driver_sdl/cairocanvas.h"
class SDLwindow
{
SDL_Window* myWindow;
SDL_Renderer* myRenderer;
SDL_PixelFormat* myFormat;
IVD::CairoCanvas myCanvas;
void* context;
void createWindow(const std::string title, const IVD::Rect theRect, const int flags);
void destroyWindow();
public:
SDLwindow(IVD::DisplayItem* item);
~SDLwindow();
int getWindowId()
{ return SDL_GetWindowID(myWindow); }
int getDisplayIndex()
{ return SDL_GetWindowDisplayIndex(myWindow); }
IVD::Dimens getClientArea();
IVD::Dimens getWindowArea();
IVD::Coords getWindowPos();
void setClientArea(const IVD::Dimens size);
void setWindowPos(const IVD::Coords pos);
void setResizable(const bool flag);
void setVisible(const bool flag);
void setTitleText(const std::string title);
IVD::Canvas* getCanvas()
{ return &myCanvas; }
void present();
};
#endif // SDLWINDOW_H

View File

@ -0,0 +1,272 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "text.h"
#include "harfbuzz/hb.h"
#include "harfbuzz/hb-ft.h"
#include "specific_driver_sdl/cairocanvas.h"
#include "cairo/cairo.h"
#include "cairo/cairo-ft.h"
#include <map>
namespace IVD
{
namespace Text
{
class RunWrapper
{
Dimens myDimens;
Coords bearings;
const Angle itemFlowAngle;
static FT_Library myFtLib;
hb_buffer_t* harfBuzzBuffer;
unsigned int glyphCount;
hb_glyph_info_t* glyphInfo;
hb_glyph_position_t* glyphPos;
struct LoadedFont
{
hb_font_t* myHarfBuzzFont;
FT_Face freeTypeFace;
cairo_font_face_t* cairoFace;
bool ready()
{ return myHarfBuzzFont && freeTypeFace && cairoFace; }
LoadedFont(): myHarfBuzzFont(nullptr), freeTypeFace(nullptr), cairoFace(nullptr) {}
};
static std::map<Default::Filter::FontData, LoadedFont> fontCache;
LoadedFont myLoadedFont;
LoadedFont loadFace(DisplayItem* style);
bool validState;
public:
RunWrapper(DisplayItem* style, const std::string text);
~RunWrapper();
bool checkValidState()
{ return validState; }
Dimens getDimens() const
{ return myDimens; }
Coords getBearings() const
{ return bearings; }
//API inversion?
int getItemFlowDimension() const
{ return getDimens().get(itemFlowAngle); }
std::vector<cairo_glyph_t> getCairoGlyphs() const;
void prepareCairoContext(cairo_t* theCai, DisplayItem* theItem);
};
FT_Library RunWrapper::myFtLib = nullptr;
std::map<Default::Filter::FontData, RunWrapper::LoadedFont> RunWrapper::fontCache =
std::map<Default::Filter::FontData, RunWrapper::LoadedFont>();
RunWrapper::RunWrapper(DisplayItem* style, const std::string text):
harfBuzzBuffer(nullptr),
glyphInfo(nullptr),
glyphPos(nullptr),
validState(false),
itemFlowAngle(Default::Filter::getItemFlowAngle(style))
{
if(!myFtLib)
{
auto error = FT_Init_FreeType(&myFtLib);
if(error) return;
}
myLoadedFont = loadFace(style);
{
const int sizeInPt = Default::Filter::getFontSize(style);
//Something something size in pt so "dpi" is 72 by definition? Seems to be the
// same as what cairo does.
const int hdpi = 72;
const int vdpi = 72;
//Alright, so we have to set the size here, because cairo apparently caches
// the size on the font? Because if we don't do this, the next call to
// hb_shape with the same font but different size results in really weird spacing.
//The only difference being that the "cairo_set_font_size" has been called with
// the previous font, but not the current size. It *MUST* be caching it here
//Which, incidentally, means that this is exactly where we should be updating it
// anyway~
FT_Set_Char_Size(myLoadedFont.freeTypeFace, 0, sizeInPt * 64, hdpi, vdpi);
}
if(!myLoadedFont.ready()) return;
harfBuzzBuffer = hb_buffer_create();
hb_buffer_add_utf8(harfBuzzBuffer, text.c_str(), -1, 0, -1);
hb_buffer_set_direction(harfBuzzBuffer, HB_DIRECTION_LTR);
hb_buffer_set_script(harfBuzzBuffer, HB_SCRIPT_LATIN);
hb_buffer_set_language(harfBuzzBuffer, hb_language_from_string(text.c_str(), -1));
hb_shape(myLoadedFont.myHarfBuzzFont, harfBuzzBuffer, nullptr, 0);
glyphInfo = hb_buffer_get_glyph_infos(harfBuzzBuffer, &glyphCount);
glyphPos = hb_buffer_get_glyph_positions(harfBuzzBuffer, &glyphCount);
{
//absolutely arbitrary size... We just need a valid surface to test against... TODO
cairo_surface_t* surf = createNewCairoSurfaceForPlatform(Dimens(100,100));
cairo_t* myCai = cairo_create(surf);
prepareCairoContext(myCai, style);
auto glyphs = getCairoGlyphs();
cairo_text_extents_t extents;
cairo_glyph_extents(myCai, &glyphs[0], glyphs.size(), &extents);
bearings.x = extents.x_bearing;
bearings.y = extents.y_bearing;
const Angle rowFlowAngle = Default::Filter::getRowFlowAngle(style);
myDimens.get(rowFlowAngle) = rowFlowAngle == Angle::Horizontal ? extents.width
: extents.height;
//Gotta include that dank as whitespace.
myDimens.get(itemFlowAngle) = itemFlowAngle == Angle::Horizontal ? extents.x_advance
: extents.y_advance;
cairo_destroy(myCai);
cairo_surface_destroy(surf);
}
validState = true;
}
RunWrapper::LoadedFont RunWrapper::loadFace(DisplayItem *style)
{
Default::Filter::FontData fontData = Default::Filter::getFontFace(style);
auto pos = fontCache.find(fontData);
if(pos != fontCache.end())
return pos->second;
LoadedFont myLoaded;
Default::Filter::FontData myFont = Default::Filter::getFontFace(style);
auto error = FT_New_Memory_Face(myFtLib, myFont.data, myFont.size, 0, &myLoaded.freeTypeFace);
if(error == FT_Err_Unknown_File_Format) return LoadedFont();
else if(error) return LoadedFont();
myLoaded.myHarfBuzzFont = hb_ft_font_create_referenced(myLoaded.freeTypeFace);
if(error) return LoadedFont();
myLoaded.cairoFace = cairo_ft_font_face_create_for_ft_face(myLoaded.freeTypeFace, 0);
fontCache[fontData] = myLoaded;
return myLoaded;
}
RunWrapper::~RunWrapper()
{
//Alright we'll just never fucking free anything, have it your way, cairo.
hb_buffer_destroy(harfBuzzBuffer);
}
std::vector<cairo_glyph_t> RunWrapper::getCairoGlyphs() const
{
std::vector<cairo_glyph_t> cairoGlyphs;
int xAdvance = 0;
int yAdvance = 0;
for(int i = 0; i != glyphCount; ++i)
{
cairo_glyph_t oneCairoGlyph;
//According to the harfbuzz docs, hb_glyph_info_t::codepoint refers to a unicode codepoint
// before shaping, and to the glyph index after, which is what cairo needs here.
oneCairoGlyph.index = glyphInfo[i].codepoint;
hb_glyph_position_t hbGlyphPos = glyphPos[i];
oneCairoGlyph.x = xAdvance;
oneCairoGlyph.y = yAdvance;
xAdvance += hbGlyphPos.x_advance / 64;
yAdvance += hbGlyphPos.y_advance / 64;
cairoGlyphs.push_back(oneCairoGlyph);
}
return cairoGlyphs;
}
void RunWrapper::prepareCairoContext(cairo_t* theCai, DisplayItem* theItem)
{
//TODO: Would "font height" be a good attribute? It's more controllable, I guess, and certainly
// more predictable. Apparently that's what font-size is in CSS (sum of ascent and descent)
cairo_set_font_face(theCai, myLoadedFont.cairoFace);
cairo_set_font_size(theCai, Default::Filter::getFontSize(theItem));
assert(!cairo_status(theCai));
}
Dimens getRunDimensions(DisplayItem* item, const std::string text)
{
return RunWrapper(item, text).getDimens();
}
int getMaxStringLengthForSpace(DisplayItem* style, const std::string text, const int space)
{
int lastGoodSize = 0; //'member yer last goooooooooood size? Aww yeh...
for(; lastGoodSize != text.size(); ++lastGoodSize)
{
Text::RunWrapper myRun(style, text.substr(0, lastGoodSize));
if(myRun.getItemFlowDimension() > space)
return lastGoodSize ? --lastGoodSize
: lastGoodSize;
}
return lastGoodSize;
}
}//Text
void CairoCanvas::drawText(Coords origin, const std::string text, DisplayItem *style)
{
Text::RunWrapper myRun = Text::RunWrapper(style, text);
auto cairoGlyphs = myRun.getCairoGlyphs();
const Coords textPos = origin - myRun.getBearings();
drawWith(Default::Filter::getFontColor(style), Default::Filter::getAlpha(style), [&](cairo_t* cai)
{
myRun.prepareCairoContext(cai, style);
cairo_translate(cai, textPos.x, textPos.y);
//cairo_scale(cai, 1, 1);
cairo_show_glyphs(cai, &cairoGlyphs[0], cairoGlyphs.size());
auto i = cairo_status(cai);
assert(i == 0);
});
}
}//IVD

18
src/standardstatekeys.h Executable file
View File

@ -0,0 +1,18 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef STANDARDSTATEKEYS_H
#define STANDARDSTATEKEYS_H
#include <ivd/statekey.h>
namespace IVD
{
namespace StateKeys
{
static const ValueKeyPath triggerStateClicked = { "clicked" };
}//StateKeys
}//IVD
#endif // STANDARDSTATEKEYS_H

35
src/statekey.h Executable file
View File

@ -0,0 +1,35 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef IVD_STATEKEY_H
#define IVD_STATEKEY_H
#include "rustutils/lexcompare.h"
#include "valuekey.h"
namespace IVD
{
class DisplayItem;
struct StateKey
{
ValueKey identity;
void* scope;
StateKey(): scope(nullptr) {}
StateKey(const ValueKey key):
identity(key),
scope(nullptr)
{}
StateKey(const ValueKey theIdentity, void* theScope):
identity(theIdentity),
scope(theScope)
{}
RUSTUTILS_DEFINE_COMP(StateKey, identity, scope)
};
}//IVD
#endif // IVD_STATEKEY_H

178
src/statemanager.cpp Normal file
View File

@ -0,0 +1,178 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#include "statemanager.h"
namespace IVD
{
bool State::mutate(const bool setTo, const uint64_t theStamp)
{
if(active == setTo) return false;
active = setTo;
stamp = theStamp;
for(auto pair : observers) updateSingleObserver(pair.first, pair.second);
syncAffectedVirtualStateKeys();
return true;
}
void State::syncAffectedVirtualStateKeys()
{
for(auto vskey : affectedVirtualKeys)
vskey.syncProxyState(myStateManager);
}
void State::insertVirtualStateKey(VirtualStateKey vskey)
{
myVirtualStateKey = vskey;
myVirtualStateKey->syncProxyState(myStateManager);
}
State& StateManager::initState(const StateKey key)
{
if(key.scope) volatileStateMap[key.scope].push_back(key.identity);
State& theState = states[key.identity][key.scope];
theState.ughSetStateManager(this);
return theState;
}
State* StateManager::findState(const StateKey key)
{
auto rangeIt = states.find(key.identity);
if(rangeIt == states.end()) return nullptr;
StateRange& range = rangeIt->second;
auto scopeIt = range.find(key.scope);
if(scopeIt == range.end()) return nullptr;
return &scopeIt->second;
}
void StateManager::eraseState(const StateKey key)
{
states[key.identity].erase(key.scope);
}
void StateManager::eraseStateIfOrphaned(const StateKey key)
{
auto rangeIterator = states.find(key.identity);
if(rangeIterator == states.end()) return;
auto stateIt = rangeIterator->second.find(key.scope);
if(stateIt == rangeIterator->second.end()) return;
State* state = &stateIt->second;
if(!state->checkObserved())
{
if(stateIt->second.getVirtualStateKey())
{
//Honest to god I want to know if this condition is ever met.
auto vskey = *stateIt->second.getVirtualStateKey();
for(StateKey affectedStateKey : vskey.getAffectedKeys())
{
State* affectedState = findState(affectedStateKey);
if(!affectedState) continue;
affectedState->removeAffectedVirtualStateKey(vskey);
eraseStateIfOrphaned(affectedStateKey);
}
}
rangeIterator->second.erase(stateIt);
//Erase range if orphaned
if(rangeIterator->second.empty())
states.erase(rangeIterator);
}
}
void StateManager::deallocateScope(void* scope)
{
for(ValueKey key : volatileStateMap[scope])
eraseStateIfOrphaned(StateKey(key, scope));
volatileStateMap.erase(scope);
}
void StateManager::attemptMutate(State* theState, const bool flag)
{
uint64_t tryStamp = lastStamp + 1;
if(theState->mutate(flag, tryStamp))
{
lastStamp = tryStamp; //Commit
}
}
void StateManager::removeReferencesToDisplayItem(DisplayItem* item)
{
//Remove any states on the item.
deallocateScope(item);
//Remove item from any states it observes.
for(StateKey key : displayItemObserverMap[item])
{
State* state = findState(key);
findState(key)->removeObserver(item);
eraseStateIfOrphaned(key);
}
displayItemObserverMap.erase(item);
}
void StateManager::insertVirtualState(VirtualStateKey vskey)
{
State* proxyState = findState(vskey.proxyStateKey);
assert(proxyState); //I mean it could be a no-op otherwise, but why?
proxyState->insertVirtualStateKey(vskey);
for(StateKey key : vskey.getAffectedKeys())
{
State* affectedState = findState(key);
//If the affected state doesn't exist, it means that this hasn't been run
// and or the state is not observed directly by a DisplayItem. But it could
// still be mutated and virtual keys count as observers.
if(!affectedState) affectedState = &initState(key);
affectedState->registerAffectedVirtualStateKey(vskey);
}
}
bool StateManager::checkAny(const StateKey key)
{
for(auto range : states.at(key.identity))
{
if(range.second.check())
return true;
}
return false;
}
bool StateManager::mutateIfObserved(const StateKey key, const bool active)
{
auto state = findState(key);
if(!state) return false;
assert(state->checkObserved());
attemptMutate(state, active);
return true;
}
void StateManager::mutateAll(const StateKey key, const bool active)
{
for(auto& pair : states[key.identity])
{
void* scope = pair.first;
State& state = pair.second;
//Ignore the global, it's semantically independent and inherently singular
if(!scope) continue;
attemptMutate(&state, active);
}
}
}//IVD

156
src/statemanager.h Executable file
View File

@ -0,0 +1,156 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef STATEMANAGER_H
#define STATEMANAGER_H
#include <set>
#include "assert.h"
#include "statekey.h"
#include "virtualstatekey.h"
#include "displayitem.h"
#include "element.h"
namespace IVD
{
class StateManager;
class State
{
//The only important thing a state does is associate a
// set of attributes to the current system state.
//So why not store the attribute set along with the
// observer of this state, and tell it when
// they've changed.
bool active;
uint64_t stamp;
std::map<DisplayItem*, AttributePositionPair> observers;
StateManager* myStateManager;
std::optional<VirtualStateKey> myVirtualStateKey;
std::set<VirtualStateKey> affectedVirtualKeys;
void updateSingleObserver(DisplayItem* item, AttributePositionPair pair)
{
if(active) item->addAttributeSet(pair);
else item->removeAttributeSet(pair);
}
public:
State(): active(false), stamp(0) {}
void ughSetStateManager(StateManager* statemanager)
{ myStateManager = statemanager; }
void reigsterObserver(DisplayItem* item, AttributePositionPair pair)
{
observers[item] = pair;
updateSingleObserver(item, pair);
}
int checkObserved() { return observers.size() || affectedVirtualKeys.size(); }
void removeObserver(DisplayItem* item) { observers.erase(item); }
bool mutate(const bool setTo, const uint64_t theStamp);
void syncAffectedVirtualStateKeys();
void insertVirtualStateKey(VirtualStateKey vskey);
void registerAffectedVirtualStateKey(VirtualStateKey key)
{ affectedVirtualKeys.insert(key); }
void removeAffectedVirtualStateKey(VirtualStateKey vskey)
{ affectedVirtualKeys.erase(vskey); }
std::optional<VirtualStateKey> getVirtualStateKey()
{ return myVirtualStateKey; }
bool check() { return active; }
uint64_t getStamp() { return stamp; }
};
class StateManager
{
//First stamp is always 1. Unstamped states are == 0.
uint64_t lastStamp;
//What about compound states...
typedef std::map<void*, State> StateRange;
std::map<ValueKey, StateRange> states;
std::vector<StateKey> triggerStates;
std::map<void*, std::vector<ValueKey>> volatileStateMap;
std::map<DisplayItem*, std::vector<StateKey>> displayItemObserverMap;
State& initState(const StateKey key);
State* findState(const StateKey key);
void eraseState(const StateKey key);
void eraseStateIfOrphaned(const StateKey key);
void deallocateScope(void* scope);
void attemptMutate(State* theState, const bool flag);
public:
StateManager(): lastStamp(0) {}
void removeReferencesToDisplayItem(DisplayItem* item);
//void removeReferencesToModel(ModelItemBase* scope)
//{ deallocateScope(scope); }
void registerStateObserver(const StateKey key,
DisplayItem* observer,
AttributePositionPair pair)
{
displayItemObserverMap[observer].push_back(key);
initState(key).reigsterObserver(observer, pair);
}
void insertVirtualState(VirtualStateKey vskey);
bool checkState(const StateKey key)
{
State* state = findState(key);
if(!state) return false;
return state->check();
}
bool checkAny(const StateKey key);
uint64_t getStamp(const StateKey key)
{
State* state = findState(key);
if(!state) return 0;
return state->getStamp();
}
uint64_t getLastStamp()
{ return lastStamp; }
//False just means it's not observed, not that it mutated, specifically.
//Kinda shoddy... The only thing that needs this is setTriggerIfObserved...
bool mutateIfObserved(const StateKey key, const bool active);
void mutateAll(const StateKey key, const bool active);
void setTriggerIfObserved(const StateKey key)
{
if(mutateIfObserved(key, true))
triggerStates.push_back(key);
}
void resetTriggerStates()
{
for(StateKey key : triggerStates)
attemptMutate(findState(key), false);
triggerStates.clear();
}
bool hasTriggerStates()
{ return triggerStates.size(); }
};
}//IVD
#endif // STATEMANAGER_H

272
src/states.h Normal file
View File

@ -0,0 +1,272 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#ifndef STATES_H
#define STATES_H
#include <string>
namespace IVD
{
namespace States
{
typedef std::string Symbol;
enum class StateType
{
Press,
Release,
Active,
};
enum class KeyType
{
Positional,
Literal,
};
//Just for consistency, to pretend that the below
// are just generic constants. WHICH YOU SHOULD!!!
inline Symbol getButton(const Symbol button)
{ return button; }
inline Symbol getButtonActive(const Symbol button)
{ return (std::string(button) + "-Active").c_str(); }
inline Symbol getButtonPress(const Symbol button)
{ return (std::string(button) + "-Press").c_str(); }
inline Symbol getButtonRelease(const Symbol button)
{ return (std::string(button) + "-Release").c_str(); }
namespace Item
{
const Symbol HoverExclusive = "IVD-Item-Hover";
const Symbol HoverInclusive = "IVD-Item-Hover-Inclusive";
//const Symbol GeometryChanged = "IVD-Item-Geometry-Changed";
const Symbol ModelChanged = "Model-Changed";
}//Item
namespace Window
{
/** \brief */
//These are only set on the display item.
/** \brief State: Set while the window is visible. Item specific.*/
const Symbol Visible = "IVD-Window-Visible";
/** \brief State: Set while the window is hidden, Item specific.*/
const Symbol Hidden = "IVD-Window-Hidden";
/** \brief Trigger State: Set when the window is shown. Item specific.*/
const Symbol Shown = "IVD-Window-Shown";
/** \brief Trigger State: This is set after a window is initialized,
* and the geometry is set for the first time. Item specific.*/
const Symbol Initialized = "IVD-Window-Initialized";
//Is there a difference between mouse and keyboard focus?
//(mouse focus just means the mouse is over it?)
/** \brief State: Set while the window has keyboard focus. Item Specific.*/
const Symbol Focused = "IVD-Window-Focused";
/** \brief State: Set while the window has mouse focus. Item Specific.*/
const Symbol MouseFocus = "IVD-Window-Focus-Mouse";
/** \brief Trigger State: Set when the window is minimized. Item Specific.*/
const Symbol Minimized = "IVD-Window-Minimized";
/** \brief Trigger State: Set when the window is maximized. Item Specific.*/
const Symbol Maximized = "IVD-Window-Maximized";
/** \brief Trigger State: Set when the close button is pressed on a window. Item Specific.*/
const Symbol CloseRequest = "IVD-Window-Close";
}//Window
namespace App
{
/** \brief Trigger State: Set when the application receives a close event,
* in addition to IVD-Window-Close for the last window.*/
const Symbol CloseApp = "IVD-Close";
}//App
namespace Key
{
//Keyboards are more standard than you'd expect,
// but perhaps not as standard as they should you'd hope be wise.
//Which one is ze any key?
const Symbol Any = "IVD-Any-Key";
const Symbol Play = "IVD-Key-Media-Play";
const Symbol Stop = "IVD-Key-Media-Stop";
const Symbol Previous = "IVD-Key-Media-Previous";
const Symbol Next = "IVD-Key-Media-Next";
const Symbol LeftSuper = "IVD-Key-Left-Super";
const Symbol LeftShift = "IVD-Key-Left-Shift";
const Symbol LeftCtrl = "IVD-Key-Left-Ctrl";
const Symbol LeftAlt = "IVD-Key-Left-Alt";
const Symbol RightSuper = "IVD-Key-Right-Super";
const Symbol RightShift = "IVD-Key-Right-Shift";
const Symbol RightCtrl = "IVD-Key-Right-Ctrl";
const Symbol RightAlt = "IVD-Key-Right-Alt";
const Symbol CapsLock = "IVD-Key-Caps-Lock";
const Symbol AltGr = "IVD-Key-Alt-Gr";
const Symbol Menu = "IVD-Key-Menu";
const Symbol PrintScreen = "IVD-Key-Print-Screen";
const Symbol ScrollLock = "IVD-Key-Scroll-Lock";
const Symbol Pause = "IVD-Key-Pause";
const Symbol Insert = "IVD-Key-Insert";
const Symbol Delete = "IVD-Key-Delete";
const Symbol Home = "IVD-Key-Home";
const Symbol End = "IVD-Key-End";
const Symbol PageUp = "IVD-Key-Page-Up";
const Symbol PageDown = "IVD-Key-Page-Down";
const Symbol LeftArrow = "IVD-Key-Arrow-Left";
const Symbol RightArrow = "IVD-Key-Arrow-Right";
const Symbol UpArrow = "IVD-Key-Arrow-Up";
const Symbol DownArrow = "IVD-Key-Arrow-Down";
const Symbol NumLock = "IVD-Key-Num-Lock";
const Symbol Escape = "IVD-Key-Escape";
const Symbol F1 = "IVD-Key-F1";
const Symbol F2 = "IVD-Key-F2";
const Symbol F3 = "IVD-Key-F3";
const Symbol F4 = "IVD-Key-F4";
const Symbol F5 = "IVD-Key-F5";
const Symbol F6 = "IVD-Key-F6";
const Symbol F7 = "IVD-Key-F7";
const Symbol F8 = "IVD-Key-F8";
const Symbol F9 = "IVD-Key-F9";
const Symbol F10 = "IVD-Key-F10";
const Symbol F11 = "IVD-Key-F11";
const Symbol F12 = "IVD-Key-F12";
//Row 0
const Symbol Tilde = "IVD-Scan-Tilde";
const Symbol RowNum1 = "IVD-Scan-Row-Num-1";
const Symbol RowNum2 = "IVD-Scan-Row-Num-2";
const Symbol RowNum3 = "IVD-Scan-Row-Num-3";
const Symbol RowNum4 = "IVD-Scan-Row-Num-4";
const Symbol RowNum5 = "IVD-Scan-Row-Num-5";
const Symbol RowNum6 = "IVD-Scan-Row-Num-6";
const Symbol RowNum7 = "IVD-Scan-Row-Num-7";
const Symbol RowNum8 = "IVD-Scan-Row-Num-8";
const Symbol RowNum9 = "IVD-Scan-Row-Num-9";
const Symbol RowNum0 = "IVD-Scan-Row-Num-0";
const Symbol Minus = "IVD-Scan-Minus";
const Symbol Equals = "IVD-Scan-Equals";
const Symbol Backspace = "IVD-Scan-Backspace";
//Row 1
const Symbol Tab = "IVD-Scan-Tab";
const Symbol Q = "IVD-Scan-Q";
const Symbol W = "IVD-Scan-W";
const Symbol E = "IVD-Scan-E";
const Symbol R = "IVD-Scan-R";
const Symbol T = "IVD-Scan-T";
const Symbol Y = "IVD-Scan-Y";
const Symbol U = "IVD-Scan-U";
const Symbol I = "IVD-Scan-I";
const Symbol O = "IVD-Scan-O";
const Symbol P = "IVD-Scan-P";
const Symbol LeftBracket = "IVD-Scan-Left-Bracket";
const Symbol RightBracket = "IVD-Scan-Right-Bracket";
const Symbol Backslash = "IVD-Scan-Backslash";
//Row 2
const Symbol A = "IVD-Scan-A";
const Symbol S = "IVD-Scan-S";
const Symbol D = "IVD-Scan-D";
const Symbol F = "IVD-Scan-F";
const Symbol G = "IVD-Scan-G";
const Symbol H = "IVD-Scan-H";
const Symbol J = "IVD-Scan-J";
const Symbol K = "IVD-Scan-K";
const Symbol L = "IVD-Scan-L";
const Symbol Semicolon = "IVD-Scan-Semicolon";
const Symbol Apostrophe = "IVD-Scan-Apostrophe";
const Symbol Return = "IVD-Scan-Return";
//Row 3
const Symbol Z = "IVD-Scan-Z";
const Symbol X = "IVD-Scan-X";
const Symbol C = "IVD-Scan-C";
const Symbol V = "IVD-Scan-V";
const Symbol B = "IVD-Scan-B";
const Symbol N = "IVD-Scan-N";
const Symbol M = "IVD-Scan-M";
const Symbol Comma = "IVD-Scan-Comma";
const Symbol Period = "IVD-Scan-Period";
const Symbol Slash = "IVD-Scan-Slash";
//Row 4
const Symbol Spacebar = "IVD-Scan-Spacebar";
namespace Pad
{
//Just going to start with basics
const Symbol Slash = "IVD-Scan-Pad-Slash";
const Symbol Star = "IVD-Scan-Pad-Star";
const Symbol Minus = "IVD-Scan-Pad-Minus";
const Symbol Num7 = "IVD-Scan-Pad-7";
const Symbol Num8 = "IVD-Scan-Pad-8";
const Symbol Num9 = "IVD-Scan-Pad-9";
const Symbol Plus = "IVD-Scan-Pad-Plus";
const Symbol Num4 = "IVD-Scan-Pad-4";
const Symbol Num5 = "IVD-Scan-Pad-5";
const Symbol Num6 = "IVD-Scan-Pad-6";
const Symbol Tab = "IVD-Scan-Pad-Tab";
const Symbol Num1 = "IVD-Scan-Pad-1";
const Symbol Num2 = "IVD-Scan-Pad-2";
const Symbol Num3 = "IVD-Scan-Pad-3";
const Symbol Num0 = "IVD-Scan-Pad-0";
const Symbol Period = "IVD-Scan-Pad-Period";
const Symbol Return = "IVD-Scan-Pad-Return";
}//Pad
}//Key
namespace Mouse
{
const Symbol ButtonLeft = "IVD-Mouse-Left";
const Symbol ButtonMiddle = "IVD-Mouse-Middle";
const Symbol ButtonRight = "IVD-Mouse-Right";
const Symbol ButtonFour = "IVD-Mouse-Four";
const Symbol ButtonFive = "IVD-Mouse-Five";
const Symbol Motion = "IVD-Mouse-Motion";
const Symbol Wheel = "IVD-Mouse-Wheel";
}//Mouse
}//States
}//IVD
#endif // STATES_H

View File

@ -0,0 +1,61 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#window
{
position-within: Environment;
window-size-strategy: bottom-up;
width: 640;
height: 400;
layout: stack;
state IVD-Window-Close:
trigger: IVD-Core-Quit;
}
#main-content-container -> content:
{
position-within: window;
layout: hbox;
}
//#welcome;
#sidebar
{
position-within: main-content-container;
layout: vbox;
}
#menu-item -> menu-items
{
position-within: sidebar;
text: model.menu-title;
padding: 10;
trans-x: -width;
induce-state: animating;
state animating:
trans-x: 0, ease-in(1000ms, graph(linear, 0 @ 0, 1 @ 1));
state IVD-Item-Hover & ::.IVD-Mouse-Left-Press:
trigger: model.update-content;
state IVD-Item-Hover:
color: purple;
}
#content -> content
{
position-within: main-content-container;
text: model.content;
}

18
src/tests/accept.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
#This file is part of the IVD project and is licensed under LGPL-3.0-only
compiler="./ivdserializingcompiler"
for arg in "$@"
do
dir=$(dirname $arg)
outdir=$dir/ivdserial
mkdir -p $outdir;
name=$(basename $arg)
newPath=$outdir/$name"serial"
$compiler $arg > $newPath
done

View File

@ -0,0 +1,4 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

View File

@ -0,0 +1,9 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#
{
fjkd: rie;
width: 1004;
}

View File

@ -0,0 +1,8 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#
{
align-x: center;
}

View File

@ -0,0 +1,10 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#elem
{
margin: 42, 34, 7; //Wrong number
margin: ease-in(100ms, graph(linear, 0 @ 1)); //No no
margin: delay(100ms); //NUH!
}

View File

@ -0,0 +1,9 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#
{
cell-names: jkfk ajj akakk;
bind-state: fjjfj ajja ak;
}

View File

@ -0,0 +1,10 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#
{
set: width = 4;
state s:
declare expression: f = width * 4;
}

View File

@ -0,0 +1,5 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
#{};
fsdjklfhas;jklcn
anhvfjk'nv'an fghjk; adfjkgn' wHEEEEEE

View File

@ -0,0 +1,17 @@
//This file is part of the IVD project and is licensed under the terms of the LGPL-3.0-only
spec bleeding;
#
{
layout: ...;
position-within: ...;
color: ...;
induce-state: ...;
text: ...;
height: ...;
cell-names: ...;
font: ...;
align-x: ...;
margin: ...;
}

View File

@ -0,0 +1,16 @@
The IVD compiler has encountered the following error:
Syntax error on line 19:
fjkd: rie;
^
Token is not a state or variable modifier or attribute key.
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default
------------Attr Key: size-a
Main Constraint
|
1004.000000i

View File

@ -0,0 +1,11 @@
The IVD compiler has encountered the following error:
Syntax error on line 19:
align-x: center;
^
Malformed attribute body, expected Property.
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,21 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
margin: 42, 34, 7; //Wrong number
^
Invalid number of arguments to unatural key margin, must be 1 or 4.
Syntax error on line 20:
margin: ease-in(100ms, graph(linear, 0 @ 1)); //No no
^
Unnatural key cannot contain ease specifiers.
Syntax error on line 21:
margin: delay(100ms); //NUH!
^
Unnatural key cannot contain delay specifiers.
---------------------------------------------------
=================================Element========================================
Path: elem
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,16 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
cell-names: jkfk ajj akakk;
^
Was expecting: ";", got: "User Token"
Syntax error on line 20:
bind-state: fjjfj ajja ak;
^
Was expecting: ";", got: "User Token"
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,17 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
set: width = 4;
^
Set not allowed in default state.
Syntax error on line 21:
declare expression: f = width * 4;
^
Declare not allowed in non-default state.
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default
-----------------------------State Key: this.s

View File

@ -0,0 +1,7 @@
The IVD compiler has encountered the following error:
Syntax error on line 15:
#{};
^
Expected Spec declaration.
---------------------------------------------------

View File

@ -0,0 +1,56 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
layout: ...;
^
Malformed attribute body, expected User Token, or Property.
Syntax error on line 20:
position-within: ...;
^
Was expecting: "User Token", got: "."
Syntax error on line 21:
color: ...;
^
Malformed attribute body, expected Color Literal.
Syntax error on line 22:
induce-state: ...;
^
Was expecting: "User Token", got: "."
Syntax error on line 23:
text: ...;
^
Invalid value for key, expected user token or attribute key.
Syntax error on line 24:
height: ...;
^
Malformed Expression, expected Scalar or Value Key.
Syntax error on line 25:
cell-names: ...;
^
Malformed attribute body, expected User Token List.
Syntax error on line 26:
font: ...;
^
Malformed attribute body, expected String Literal, or Property.
Syntax error on line 27:
align-x: ...;
^
Malformed attribute body, expected Property.
Syntax error on line 28:
margin: ...;
^
Malformed Expression, expected Scalar or Value Key.
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,16 @@
The IVD compiler has encountered the following error:
Syntax error on line 19:
height: 1004.;
^
Malformed Expression, expected Scalar or Value Key.
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default
------------Attr Key: size-a
Main Constraint
|
100.000000i

View File

@ -0,0 +1,20 @@
The IVD compiler has encountered the following errors
Syntax error on line 17:
# : .class1, .class2;
^
Was expecting: "User Token", got: "."
Syntax error on line 18:
# : .class2, .class1;
^
Was expecting: "User Token", got: "."
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default
=================================Element========================================
Path: anonymous-1-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,16 @@
The IVD compiler has encountered the following error:
Syntax error on line 19:
width: model.widthat; //shit shit shit models can have this names fuck fuck
^
Model scoped value key in non-enumerated element.
---------------------------------------------------
=================================Element========================================
Path: anonymous-1-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default
------------Attr Key: size-a
Main Constraint
|
model.widthat

View File

@ -0,0 +1,20 @@
The IVD compiler has encountered the following errors
Syntax error on line 18:
.class;
^
Class name conflicts with previous definition on line: 17:
.class;
^
Syntax error on line 21:
#element;
^
Element name conflicts with previous definition on line: 20:
#element;
^
---------------------------------------------------
=================================Element========================================
Path: element
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,11 @@
The IVD compiler has encountered the following error:
Syntax error on line 3:
align-horizontal: center;
^
Malformed attribute body, expected Property.
---------------------------------------------------
=================================Element========================================
Path: anonymous-0-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,25 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
align-x: outer;
^
Malformed attribute body, expected Property.
Syntax error on line 20:
justify: center;
^
Malformed attribute body, expected Property.
Syntax error on line 25:
align-y: center;
^
Malformed attribute body, expected Property.
---------------------------------------------------
=================================Element========================================
Path: i
------------------------------------------Body
-----------------------------State default
=================================Element========================================
Path: text1
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,16 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
margin: 42, 34, 7;
^
Invalid number of arguments to unatural key margin, must be 1 or 4.
Syntax error on line 20:
margin: delay(100ms);
^
Unnatural key cannot contain delay specifiers.
---------------------------------------------------
=================================Element========================================
Path: elem
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,11 @@
The IVD compiler has encountered the following error:
Syntax error on line 19:
@top
^
Remora host class already finalized.
---------------------------------------------------
=================================Element========================================
Path: topInstance
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,25 @@
The IVD compiler has encountered the following errors
Syntax error on line 21:
@host : host1;
^
Remorial instance does not declare a model.
Syntax error on line 18:
@host1 -> @::ayee;
^
But remora requires one.
---------------------------------------------------
=================================Element========================================
Path: instance
Model x
------------------------------------------Body
-----------------------------State default
=================================Element========================================
Path: instance::anonymous-3-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default
=================================Element========================================
Path: instance::anonymous-3-HORRIBLY-MAANGLED::anonymous-1-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,20 @@
The IVD compiler has encountered the following errors
Syntax error on line 19:
#instance : host;
^
Remorial instance does not declare a model.
Syntax error on line 18:
@host -> @::ayee;
^
But remora requires one.
---------------------------------------------------
=================================Element========================================
Path: instance
------------------------------------------Body
-----------------------------State default
=================================Element========================================
Path: instance::anonymous-1-HORRIBLY-MAANGLED
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,11 @@
The IVD compiler has encountered the following error:
Syntax error on line 24:
@top
^
Remora host class already finalized.
---------------------------------------------------
=================================Element========================================
Path: topInstance
------------------------------------------Body
-----------------------------State default

View File

@ -0,0 +1,7 @@
The IVD compiler has encountered the following error:
Syntax error on line 19:
@level4.remora : level3;
^
Remora host class undefined.
---------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More