Merge branch 'master' into beta
@ -70,6 +70,7 @@ option(USE_SHARED_FREETYPE "Use shared FreeType library" off)
|
||||
option(USE_SHARED_ALLEGRO4 "Use shared Allegro 4 library (without resize support)" off)
|
||||
option(ENABLE_MEMLEAK "Enable memory-leaks detector (only for developers)" off)
|
||||
option(ENABLE_UPDATER "Enable automatic check for updates" on)
|
||||
option(ENABLE_SCRIPTING "Compile with scripting support" on)
|
||||
option(ENABLE_WEBSERVER "Enable support to run a webserver (for HTML5 gamedev)" off)
|
||||
option(ENABLE_TESTS "Enable the unit tests" off)
|
||||
option(ENABLE_TRIAL_MODE "Compile the trial version" off)
|
||||
@ -163,9 +164,16 @@ else()
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
# Fix to compile gtest with VC11 (2012)
|
||||
if(MSVC_VERSION EQUAL 1700)
|
||||
add_definitions(-D_VARIADIC_MAX=10)
|
||||
if(MSVC)
|
||||
if(USE_STATIC_LIBC)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MTd")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MTd")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MT")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MT")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@ -438,6 +446,10 @@ if(ENABLE_MEMLEAK)
|
||||
add_definitions(-DLAF_MEMLEAK)
|
||||
endif()
|
||||
|
||||
if(NOT USE_STATIC_LIBC)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "Use shared (DLL) run-time lib even when Google Test is built as static lib.")
|
||||
endif()
|
||||
|
||||
set(LAF_WITH_TESTS ${ENABLE_TESTS} CACHE BOOL "Enable LAF tests")
|
||||
add_subdirectory(laf)
|
||||
|
||||
|
@ -77,8 +77,7 @@ And it uses the following third-party libraries:
|
||||
|
||||
* [Allegro 4](http://alleg.sourceforge.net/) - [allegro4 license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/allegro4-LICENSE.txt)
|
||||
* [FreeType](http://www.freetype.org/) - [FTL license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/FTL.txt)
|
||||
* [Google Test](https://github.com/google/googletest) - [gtest license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/gtest-LICENSE.txt)
|
||||
* [XFree86](http://www.x.org/) - [XFree86 license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/XFree86-LICENSE.txt)
|
||||
* [Google Test](https://github.com/google/googletest) - [BSD-like license](https://github.com/aseprite/googletest/blob/master/googletest/LICENSE)
|
||||
* [curl](http://curl.haxx.se/) - [curl license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/curl-LICENSE.txt)
|
||||
* [duktape](http://duktape.org/) - [MIT license](https://github.com/aseprite/aseprite/tree/master/third_party/duktape/LICENSE.txt)
|
||||
* [giflib](http://sourceforge.net/projects/giflib/) - [giflib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/giflib-LICENSE.txt)
|
||||
@ -86,9 +85,9 @@ And it uses the following third-party libraries:
|
||||
* [libpng](http://www.libpng.org/pub/png/) - [libpng license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/libpng-LICENSE.txt)
|
||||
* [libwebp](https://developers.google.com/speed/webp/) - [libwebp license](https://chromium.googlesource.com/webm/libwebp/+/master/COPYING)
|
||||
* [loadpng](http://tjaden.strangesoft.net/loadpng/) - [zlib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/ZLIB.txt)
|
||||
* [modp_b64](https://github.com/aseprite/aseprite/tree/master/third_party/modp_b64/modp_b64.h) - [BSD license](https://github.com/aseprite/aseprite/tree/master/third_party/modp_b64/LICENSE)
|
||||
* [pixman](http://www.pixman.org/) - [MIT license](http://cgit.freedesktop.org/pixman/plain/COPYING)
|
||||
* [simpleini](https://github.com/aseprite/simpleini/) - [MIT license](https://github.com/aseprite/simpleini/blob/aseprite/LICENCE.txt)
|
||||
* [stringencoders](https://github.com/client9/stringencoders) - [MIT license](https://github.com/aseprite/stringencoders/blob/master/LICENSE)
|
||||
* [tinyxml](http://www.sourceforge.net/projects/tinyxml) - [zlib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/ZLIB.txt)
|
||||
* [zlib](http://www.gzip.org/zlib/) - [ZLIB license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/ZLIB.txt)
|
||||
|
||||
@ -103,8 +102,7 @@ This program is distributed under three different licenses:
|
||||
(e.g. [laf](https://github.com/aseprite/laf),
|
||||
[clip](https://github.com/aseprite/clip),
|
||||
[she](https://github.com/aseprite/aseprite/tree/master/src/she),
|
||||
[gfx](https://github.com/aseprite/gfx),
|
||||
[ui](https://github.com/aseprite/ui), etc.).
|
||||
[gfx](src/gfx), [ui](src/ui), etc.).
|
||||
2. You can request a special
|
||||
[educational license](http://www.aseprite.org/faq/#is-there-an-educational-license)
|
||||
in case you are a teacher in an educational institution and want to
|
||||
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 259 B |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 189 B |
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 362 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 397 B |
@ -99,7 +99,7 @@
|
||||
<option id="rewind_on_stop" type="bool" default="false" />
|
||||
<option id="expand_menubar_on_mouseover" type="bool" default="false" migrate="Options.ExpandMenuBarOnMouseover" />
|
||||
<option id="data_recovery" type="bool" default="true" />
|
||||
<option id="data_recovery_period" type="int" default="2" />
|
||||
<option id="data_recovery_period" type="double" default="2.0" />
|
||||
<option id="show_full_path" type="bool" default="true" />
|
||||
<option id="timeline_position" type="TimelinePosition" default="TimelinePosition::BOTTOM" />
|
||||
</section>
|
||||
@ -278,6 +278,9 @@
|
||||
<option id="color1" type="app::Color" default="app::Color::fromRgb(128, 128, 128)" migrate="Option.CheckedBgColor1" />
|
||||
<option id="color2" type="app::Color" default="app::Color::fromRgb(192, 192, 192)" migrate="Option.CheckedBgColor2" />
|
||||
</section>
|
||||
<section id="timeline">
|
||||
<option id="first_frame" type="int" default="1" />
|
||||
</section>
|
||||
<section id="thumbnails">
|
||||
<option id="zoom" type="double" default="1" />
|
||||
<option id="enabled" type="bool" default="false" />
|
||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.6 KiB |
22
data/widgets/open_sequence.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<!-- ASEPRITE -->
|
||||
<!-- Copyright (C) 2016 by David Capello -->
|
||||
<gui>
|
||||
<window text="Notice" id="open_sequence">
|
||||
<vbox>
|
||||
<label text="Do you want to load the following files as an animation?" />
|
||||
<view expansive="true" id="view" minwidth="128" minheight="64">
|
||||
<listbox id="files" multiselect="true" />
|
||||
</view>
|
||||
<separator horizontal="true" />
|
||||
<check id="repeat" text="Do the same for other files" />
|
||||
<hbox>
|
||||
<boxfiller />
|
||||
<hbox homogeneous="true">
|
||||
<button id="agree" text="&Agree" closewindow="true" minwidth="60" />
|
||||
<button id="skip" text="&Skip" closewindow="true" minwidth="60" magnet="true" />
|
||||
</hbox>
|
||||
<boxfiller />
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
||||
</gui>
|
@ -43,6 +43,9 @@
|
||||
<hbox>
|
||||
<check text="Automatically save recovery data every" id="enable_data_recovery" tooltip="With this option you can recover your documents if the program finalizes unexpectedly." />
|
||||
<combobox id="data_recovery_period">
|
||||
<listitem text="10 seconds" value="0.33" />
|
||||
<listitem text="30 seconds" value="0.5" />
|
||||
<listitem text="1 Minutes" value="1" />
|
||||
<listitem text="2 Minutes" value="2" />
|
||||
<listitem text="5 Minutes" value="5" />
|
||||
<listitem text="10 Minutes" value="10" />
|
||||
@ -79,6 +82,10 @@
|
||||
<separator text="Timeline" horizontal="true" />
|
||||
<check text="Show timeline automatically" id="autotimeline" tooltip="Show the timeline automatically when a new frame or layer is added." />
|
||||
<check text="Rewind on Stop" id="rewind_on_stop" tooltip="The 'Stop' button should rewind the animation where it was started." />
|
||||
<hbox>
|
||||
<label text="Default First Frame:" />
|
||||
<entry id="first_frame" maxsize="3" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<!-- Cursors -->
|
||||
|
@ -1,13 +1,13 @@
|
||||
<!-- ASEPRITE -->
|
||||
<!-- Copyright (C) 2014 by David Capello -->
|
||||
<!-- Copyright (C) 2014-2016 by David Capello -->
|
||||
<gui>
|
||||
<vbox id="palette_popup">
|
||||
<label text="Available Palettes:" />
|
||||
<view id="view" expansive="true" />
|
||||
<hbox>
|
||||
<button id="load_pal" text="Load" width="80" />
|
||||
<button id="load_pal" text="&Load" width="80" magnet="true" />
|
||||
<hbox expansive="true" />
|
||||
<button id="open_folder" text="Open Folder" width="60" />
|
||||
<button id="open_folder" text="Open &Folder" width="60" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
</gui>
|
||||
|
@ -2,30 +2,35 @@
|
||||
<!-- Copyright (C) 2014-2016 by David Capello -->
|
||||
<gui>
|
||||
<vbox id="timeline_conf">
|
||||
|
||||
<hbox>
|
||||
<vbox>
|
||||
<separator cell_hspan="2" text="Position:" left="true" horizontal="true" />
|
||||
<hbox>
|
||||
<buttonset columns="2" id="position">
|
||||
<item text="&Left" />
|
||||
<item text="&Right" />
|
||||
<item text="&Bottom" hspan="2" />
|
||||
</buttonset>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<separator cell_hspan="2" text="Thumbnails:" left="true" horizontal="true" />
|
||||
<grid columns="3">
|
||||
<check id="thumb_enabled" text="Force" />
|
||||
<label text="Zoom:" />
|
||||
<slider min="1" max="10" id="zoom" cell_align="horizontal" width="128" />
|
||||
<vbox>
|
||||
<separator cell_hspan="2" text="Position:" left="true" horizontal="true" />
|
||||
<hbox>
|
||||
<buttonset columns="2" id="position">
|
||||
<item text="&Left" />
|
||||
<item text="&Right" />
|
||||
<item text="&Bottom" hspan="2" />
|
||||
</buttonset>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<separator cell_hspan="2" text="Frame Header:" left="true" horizontal="true" />
|
||||
<hbox>
|
||||
<label text="First Frame:" />
|
||||
<entry id="first_frame" maxsize="3" />
|
||||
</hbox>
|
||||
|
||||
<check id="thumb_overlay_enabled" text="Overlay"/>
|
||||
<label text="Size:" />
|
||||
<slider min="2" max="10" id="thumb_overlay_size" cell_align="horizontal" width="128" />
|
||||
</grid>
|
||||
</vbox>
|
||||
<separator cell_hspan="2" text="Thumbnails:" left="true" horizontal="true" />
|
||||
<grid columns="3">
|
||||
<check id="thumb_enabled" text="Force" />
|
||||
<label text="Zoom:" />
|
||||
<slider min="1" max="10" id="zoom" cell_align="horizontal" width="128" />
|
||||
|
||||
<check id="thumb_overlay_enabled" text="Overlay"/>
|
||||
<label text="Size:" />
|
||||
<slider min="2" max="10" id="thumb_overlay_size" cell_align="horizontal" width="128" />
|
||||
</grid>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<separator cell_hspan="2" text="Onion Skin:" left="true" horizontal="true" />
|
||||
|
2
laf
@ -1 +1 @@
|
||||
Subproject commit 11274341a6390f9dda085256f98567156aa21cc9
|
||||
Subproject commit 42ad03a9a30ebe619674aaf07ef27cf442133903
|
@ -97,10 +97,14 @@ add_subdirectory(gfx)
|
||||
add_subdirectory(net)
|
||||
add_subdirectory(render)
|
||||
add_subdirectory(docio)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(she)
|
||||
add_subdirectory(ui)
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
add_subdirectory(script)
|
||||
add_definitions(-DENABLE_SCRIPTING)
|
||||
endif()
|
||||
|
||||
if(ENABLE_UPDATER)
|
||||
add_subdirectory(updater)
|
||||
endif()
|
||||
|
@ -89,6 +89,23 @@ if(WITH_WEBP_SUPPORT)
|
||||
list(APPEND file_formats file/webp_format.cpp)
|
||||
endif()
|
||||
|
||||
set(scripting_files)
|
||||
if(ENABLE_SCRIPTING)
|
||||
set(scripting_files
|
||||
commands/cmd_developer_console.cpp
|
||||
commands/cmd_run_script.cpp
|
||||
script/app_object.cpp
|
||||
script/app_scripting.cpp
|
||||
script/console_object.cpp
|
||||
script/image_class.cpp
|
||||
script/image_wrap.cpp
|
||||
script/selection_class.cpp
|
||||
script/sprite_class.cpp
|
||||
script/sprite_wrap.cpp
|
||||
shell.cpp
|
||||
ui/devconsole_view.cpp)
|
||||
endif()
|
||||
|
||||
add_library(app-lib
|
||||
app.cpp
|
||||
app_brushes.cpp
|
||||
@ -191,7 +208,6 @@ add_library(app-lib
|
||||
commands/cmd_crop.cpp
|
||||
commands/cmd_cut.cpp
|
||||
commands/cmd_deselect_mask.cpp
|
||||
commands/cmd_developer_console.cpp
|
||||
commands/cmd_discard_brush.cpp
|
||||
commands/cmd_duplicate_layer.cpp
|
||||
commands/cmd_duplicate_sprite.cpp
|
||||
@ -253,7 +269,6 @@ add_library(app-lib
|
||||
commands/cmd_reselect_mask.cpp
|
||||
commands/cmd_reverse_frames.cpp
|
||||
commands/cmd_rotate.cpp
|
||||
commands/cmd_run_script.cpp
|
||||
commands/cmd_save_file.cpp
|
||||
commands/cmd_save_mask.cpp
|
||||
commands/cmd_save_palette.cpp
|
||||
@ -332,17 +347,8 @@ add_library(app-lib
|
||||
resource_finder.cpp
|
||||
restore_visible_layers.cpp
|
||||
rw_lock.cpp
|
||||
script/app_object.cpp
|
||||
script/app_scripting.cpp
|
||||
script/console_object.cpp
|
||||
script/image_class.cpp
|
||||
script/image_wrap.cpp
|
||||
script/selection_class.cpp
|
||||
script/sprite_class.cpp
|
||||
script/sprite_wrap.cpp
|
||||
send_crash.cpp
|
||||
shade.cpp
|
||||
shell.cpp
|
||||
snap_to_grid.cpp
|
||||
thumbnail_generator.cpp
|
||||
tools/active_tool.cpp
|
||||
@ -371,7 +377,6 @@ add_library(app-lib
|
||||
ui/color_wheel.cpp
|
||||
ui/configure_timeline_popup.cpp
|
||||
ui/context_bar.cpp
|
||||
ui/devconsole_view.cpp
|
||||
ui/document_view.cpp
|
||||
ui/drop_down_button.cpp
|
||||
ui/editor/brush_preview.cpp
|
||||
@ -455,6 +460,7 @@ add_library(app-lib
|
||||
xml_document.cpp
|
||||
xml_exception.cpp
|
||||
${data_recovery_files}
|
||||
${scripting_files}
|
||||
${generated_files})
|
||||
|
||||
target_link_libraries(app-lib
|
||||
@ -470,7 +476,6 @@ target_link_libraries(app-lib
|
||||
gfx-lib
|
||||
net-lib
|
||||
render-lib
|
||||
script-lib
|
||||
she
|
||||
ui-lib
|
||||
undo
|
||||
@ -482,6 +487,10 @@ target_link_libraries(app-lib
|
||||
${ZLIB_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES})
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
target_link_libraries(app-lib script-lib)
|
||||
endif()
|
||||
|
||||
if(ENABLE_UPDATER)
|
||||
target_link_libraries(app-lib updater-lib)
|
||||
endif()
|
||||
|
@ -32,9 +32,7 @@
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/recent_files.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/script/app_scripting.h"
|
||||
#include "app/send_crash.h"
|
||||
#include "app/shell.h"
|
||||
#include "app/tools/active_tool.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/backup_indicator.h"
|
||||
@ -58,7 +56,7 @@
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/site.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "script/engine_delegate.h"
|
||||
#include "render/render.h"
|
||||
#include "she/display.h"
|
||||
#include "she/error.h"
|
||||
#include "she/system.h"
|
||||
@ -67,6 +65,12 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "app/script/app_scripting.h"
|
||||
#include "app/shell.h"
|
||||
#include "script/engine_delegate.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_STEAM
|
||||
#include "steam/steam.h"
|
||||
#endif
|
||||
@ -258,6 +262,7 @@ void App::run()
|
||||
ui::Manager::getDefault()->run();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
// Start shell to execute scripts.
|
||||
if (m_isShell) {
|
||||
script::StdoutEngineDelegate delegate;
|
||||
@ -266,6 +271,7 @@ void App::run()
|
||||
Shell shell;
|
||||
shell.run(engine);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Destroy all documents in the UIContext.
|
||||
const doc::Documents& docs = m_modules->m_ui_context.documents();
|
||||
|
@ -26,7 +26,9 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
, m_showHelp(false)
|
||||
, m_showVersion(false)
|
||||
, m_verboseLevel(kNoVerbose)
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
, m_shell(m_po.add("shell").description("Start an interactive console to execute scripts"))
|
||||
#endif
|
||||
, m_batch(m_po.add("batch").mnemonic('b').description("Do not start the UI"))
|
||||
, m_preview(m_po.add("preview").mnemonic('p').description("Do not execute actions, just print what will be\ndone"))
|
||||
, m_saveAs(m_po.add("save-as").requiresValue("<filename>").description("Save the last given sprite with other format"))
|
||||
@ -54,7 +56,9 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
, m_trim(m_po.add("trim").description("Trim all images before exporting"))
|
||||
, m_crop(m_po.add("crop").requiresValue("x,y,width,height").description("Crop all the images to the given rectangle"))
|
||||
, m_filenameFormat(m_po.add("filename-format").requiresValue("<fmt>").description("Special format to generate filenames"))
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
, m_script(m_po.add("script").requiresValue("<filename>").description("Execute a specific script"))
|
||||
#endif
|
||||
, m_listLayers(m_po.add("list-layers").description("List layers of the next given sprite\nor include layers in JSON data"))
|
||||
, m_listTags(m_po.add("list-tags").description("List tags of the next given sprite sprite\nor include frame tags in JSON data"))
|
||||
, m_verbose(m_po.add("verbose").mnemonic('v').description("Explain what is being done"))
|
||||
@ -70,7 +74,9 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
else if (m_po.enabled(m_verbose))
|
||||
m_verboseLevel = kVerbose;
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
m_startShell = m_po.enabled(m_shell);
|
||||
#endif
|
||||
m_previewCLI = m_po.enabled(m_preview);
|
||||
m_showHelp = m_po.enabled(m_help);
|
||||
m_showVersion = m_po.enabled(m_version);
|
||||
|
@ -70,7 +70,9 @@ public:
|
||||
const Option& trim() const { return m_trim; }
|
||||
const Option& crop() const { return m_crop; }
|
||||
const Option& filenameFormat() const { return m_filenameFormat; }
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
const Option& script() const { return m_script; }
|
||||
#endif
|
||||
const Option& listLayers() const { return m_listLayers; }
|
||||
const Option& listTags() const { return m_listTags; }
|
||||
|
||||
@ -86,7 +88,9 @@ private:
|
||||
bool m_showVersion;
|
||||
VerboseLevel m_verboseLevel;
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
Option& m_shell;
|
||||
#endif
|
||||
Option& m_batch;
|
||||
Option& m_preview;
|
||||
Option& m_saveAs;
|
||||
@ -114,7 +118,9 @@ private:
|
||||
Option& m_trim;
|
||||
Option& m_crop;
|
||||
Option& m_filenameFormat;
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
Option& m_script;
|
||||
#endif
|
||||
Option& m_listLayers;
|
||||
Option& m_listTags;
|
||||
|
||||
|
@ -353,11 +353,13 @@ void CliProcessor::process()
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
// --script <filename>
|
||||
else if (opt == &m_options.script()) {
|
||||
std::string filename = value.value();
|
||||
m_delegate->execScript(filename);
|
||||
}
|
||||
#endif
|
||||
// --list-layers
|
||||
else if (opt == &m_options.listLayers()) {
|
||||
cof.listLayers = true;
|
||||
|
@ -18,14 +18,17 @@
|
||||
#include "app/document.h"
|
||||
#include "app/document_exporter.h"
|
||||
#include "app/file/palette_file.h"
|
||||
#include "app/script/app_scripting.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/frame_tag.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "script/engine_delegate.h"
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "app/script/app_scripting.h"
|
||||
#include "script/engine_delegate.h"
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -114,9 +117,11 @@ void DefaultCliDelegate::exportFiles(DocumentExporter& exporter)
|
||||
|
||||
void DefaultCliDelegate::execScript(const std::string& filename)
|
||||
{
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
script::StdoutEngineDelegate delegate;
|
||||
AppScripting engine(&delegate);
|
||||
engine.evalFile(filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -86,7 +86,7 @@ void AddLayer::removeLayer(Layer* group, Layer* layer)
|
||||
|
||||
static_cast<LayerGroup*>(group)->removeLayer(layer);
|
||||
group->incrementVersion();
|
||||
layer->sprite()->incrementVersion();
|
||||
group->sprite()->incrementVersion();
|
||||
|
||||
doc->notify_observers<DocumentEvent&>(&DocumentObserver::onAfterRemoveLayer, ev);
|
||||
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/ui/main_window.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/context.h"
|
||||
#include "app/document.h"
|
||||
#include "app/job.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "ui/alert.h"
|
||||
|
||||
@ -36,6 +37,11 @@ ExitCommand::ExitCommand()
|
||||
|
||||
void ExitCommand::onExecute(Context* ctx)
|
||||
{
|
||||
// Ignore ExitCommand when we are saving documents or doing a
|
||||
// background task
|
||||
if (Job::runningJobs() > 0)
|
||||
return;
|
||||
|
||||
if (ctx->hasModifiedDocuments()) {
|
||||
Command* closeAll = CommandsModule::instance()->getCommandByName(CommandId::CloseAllFiles);
|
||||
Params params;
|
||||
|
@ -9,11 +9,12 @@
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/context.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/document_api.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/transaction.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/sprite.h"
|
||||
@ -66,7 +67,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
|
||||
}
|
||||
else {
|
||||
m_target = SPECIFIC_FRAME;
|
||||
m_frame = frame_t(base::convert_to<int>(frame)-1);
|
||||
m_frame = frame_t(base::convert_to<int>(frame));
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +80,8 @@ void FramePropertiesCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
const Sprite* sprite = reader.sprite();
|
||||
|
||||
auto& docPref = Preferences::instance().document(context->activeDocument());
|
||||
int base = docPref.timeline.firstFrame();
|
||||
app::gen::FrameProperties window;
|
||||
SelectedFrames selFrames;
|
||||
|
||||
@ -101,7 +103,7 @@ void FramePropertiesCommand::onExecute(Context* context)
|
||||
}
|
||||
|
||||
case SPECIFIC_FRAME:
|
||||
selFrames.insert(m_frame);
|
||||
selFrames.insert(m_frame-base);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -110,11 +112,11 @@ void FramePropertiesCommand::onExecute(Context* context)
|
||||
return;
|
||||
|
||||
if (selFrames.size() == 1)
|
||||
window.frame()->setTextf("%d", selFrames.firstFrame()+1);
|
||||
window.frame()->setTextf("%d", selFrames.firstFrame()+base);
|
||||
else if (selFrames.ranges() == 1) {
|
||||
window.frame()->setTextf("[%d...%d]",
|
||||
selFrames.firstFrame()+1,
|
||||
selFrames.lastFrame()+1);
|
||||
selFrames.firstFrame()+base,
|
||||
selFrames.lastFrame()+base);
|
||||
}
|
||||
else
|
||||
window.frame()->setTextf("Multiple Frames");
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -137,22 +137,27 @@ class GotoFrameCommand : public GotoCommand {
|
||||
public:
|
||||
GotoFrameCommand() : GotoCommand("GotoFrame",
|
||||
"Go to Frame")
|
||||
, m_frame(0) { }
|
||||
, m_showUI(true) { }
|
||||
Command* clone() const override { return new GotoFrameCommand(*this); }
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override {
|
||||
std::string frame = params.get("frame");
|
||||
if (!frame.empty()) m_frame = strtol(frame.c_str(), NULL, 10);
|
||||
else m_frame = 0;
|
||||
if (!frame.empty()) {
|
||||
m_frame = strtol(frame.c_str(), nullptr, 10);
|
||||
m_showUI = false;
|
||||
}
|
||||
else
|
||||
m_showUI = true;
|
||||
}
|
||||
|
||||
frame_t onGetFrame(Editor* editor) override {
|
||||
if (m_frame == 0) {
|
||||
auto& docPref = editor->docPref();
|
||||
|
||||
if (m_showUI) {
|
||||
app::gen::GotoFrame window;
|
||||
|
||||
window.frame()->setTextf("%d", editor->frame()+1);
|
||||
|
||||
window.frame()->setTextf(
|
||||
"%d", editor->frame()+docPref.timeline.firstFrame());
|
||||
window.openWindowInForeground();
|
||||
if (window.closer() != window.ok())
|
||||
return editor->frame();
|
||||
@ -160,12 +165,11 @@ protected:
|
||||
m_frame = window.frame()->textInt();
|
||||
}
|
||||
|
||||
return MID(0, m_frame-1, editor->sprite()->lastFrame());
|
||||
return MID(0, m_frame-docPref.timeline.firstFrame(), editor->sprite()->lastFrame());
|
||||
}
|
||||
|
||||
private:
|
||||
// The frame to go. 0 is "show the UI dialog", another value is the
|
||||
// frame (1 is the first name for the user).
|
||||
bool m_showUI;
|
||||
int m_frame;
|
||||
};
|
||||
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
KeyItem(const std::string& text, Key* key, AppMenuItem* menuitem, int level)
|
||||
: ListItem(text)
|
||||
, m_key(key)
|
||||
, m_keyOrig(key ? new Key(*key): NULL)
|
||||
, m_keyOrig(key ? new Key(*key): nullptr)
|
||||
, m_menuitem(menuitem)
|
||||
, m_level(level)
|
||||
, m_hotAccel(-1)
|
||||
@ -73,6 +73,10 @@ public:
|
||||
setBorder(border);
|
||||
}
|
||||
|
||||
~KeyItem() {
|
||||
delete m_keyOrig;
|
||||
}
|
||||
|
||||
Key* key() { return m_key; }
|
||||
AppMenuItem* menuitem() const { return m_menuitem; }
|
||||
|
||||
@ -381,6 +385,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~KeyboardShortcutsWindow() {
|
||||
deleteAllKeyItems();
|
||||
}
|
||||
|
||||
void restoreKeys() {
|
||||
for (KeyItem* keyItem : m_allKeyItems) {
|
||||
keyItem->restoreKeys();
|
||||
@ -389,21 +397,12 @@ public:
|
||||
|
||||
private:
|
||||
void deleteAllKeyItems() {
|
||||
while (searchList()->lastChild())
|
||||
searchList()->removeChild(searchList()->lastChild());
|
||||
while (menus()->lastChild())
|
||||
menus()->removeChild(menus()->lastChild());
|
||||
while (commands()->lastChild())
|
||||
commands()->removeChild(commands()->lastChild());
|
||||
while (tools()->lastChild())
|
||||
tools()->removeChild(tools()->lastChild());
|
||||
while (actions()->lastChild())
|
||||
actions()->removeChild(actions()->lastChild());
|
||||
|
||||
for (KeyItem* keyItem : m_allKeyItems) {
|
||||
delete keyItem;
|
||||
}
|
||||
m_allKeyItems.clear();
|
||||
deleteList(searchList());
|
||||
deleteList(menus());
|
||||
deleteList(commands());
|
||||
deleteList(tools());
|
||||
deleteList(actions());
|
||||
ASSERT(m_allKeyItems.empty());
|
||||
}
|
||||
|
||||
void fillAllLists() {
|
||||
@ -468,9 +467,21 @@ private:
|
||||
updateViews();
|
||||
}
|
||||
|
||||
void deleteList(Widget* listbox) {
|
||||
while (listbox->lastChild()) {
|
||||
Widget* item = listbox->lastChild();
|
||||
listbox->removeChild(item);
|
||||
|
||||
auto it = std::find(m_allKeyItems.begin(), m_allKeyItems.end(), item);
|
||||
if (it != m_allKeyItems.end())
|
||||
m_allKeyItems.erase(it);
|
||||
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void fillSearchList(const std::string& search) {
|
||||
while (searchList()->lastChild())
|
||||
searchList()->removeChild(searchList()->lastChild());
|
||||
deleteList(searchList());
|
||||
|
||||
std::vector<std::string> parts;
|
||||
base::split_string(base::string_to_lower(search), parts, " ");
|
||||
@ -504,6 +515,8 @@ private:
|
||||
new KeyItem(itemText,
|
||||
keyItem->key(),
|
||||
keyItem->menuitem(), 0);
|
||||
|
||||
m_allKeyItems.push_back(copyItem);
|
||||
searchList()->addChild(copyItem);
|
||||
}
|
||||
}
|
||||
@ -586,6 +599,7 @@ private:
|
||||
menuItem->text().c_str(),
|
||||
menuItem->key(), menuItem, level);
|
||||
|
||||
m_allKeyItems.push_back(keyItem);
|
||||
listbox->addChild(keyItem);
|
||||
|
||||
if (menuItem->hasSubmenu())
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/commands/cmd_open_file.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
@ -32,22 +34,7 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
class OpenFileCommand : public Command {
|
||||
public:
|
||||
OpenFileCommand();
|
||||
Command* clone() const override { return new OpenFileCommand(*this); }
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
||||
private:
|
||||
std::string m_filename;
|
||||
std::string m_folder;
|
||||
};
|
||||
|
||||
class OpenFileJob : public Job, public IFileOpProgress
|
||||
{
|
||||
class OpenFileJob : public Job, public IFileOpProgress {
|
||||
public:
|
||||
OpenFileJob(FileOp* fop)
|
||||
: Job("Loading file")
|
||||
@ -91,6 +78,8 @@ OpenFileCommand::OpenFileCommand()
|
||||
: Command("OpenFile",
|
||||
"Open Sprite",
|
||||
CmdRecordableFlag)
|
||||
, m_repeatCheckbox(false)
|
||||
, m_seqDecision(SequenceDecision::Ask)
|
||||
{
|
||||
}
|
||||
|
||||
@ -98,12 +87,15 @@ void OpenFileCommand::onLoadParams(const Params& params)
|
||||
{
|
||||
m_filename = params.get("filename");
|
||||
m_folder = params.get("folder"); // Initial folder
|
||||
m_repeatCheckbox = (params.get("repeat_checkbox") == "true");
|
||||
}
|
||||
|
||||
void OpenFileCommand::onExecute(Context* context)
|
||||
{
|
||||
Console console;
|
||||
|
||||
m_usedFiles.clear();
|
||||
|
||||
// interactive
|
||||
if (context->isUIAvailable() && m_filename.empty()) {
|
||||
std::string exts = get_readable_extensions();
|
||||
@ -118,9 +110,23 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
}
|
||||
|
||||
if (!m_filename.empty()) {
|
||||
int flags = (m_repeatCheckbox ? FILE_LOAD_SEQUENCE_ASK_CHECKBOX: 0);
|
||||
|
||||
switch (m_seqDecision) {
|
||||
case SequenceDecision::Ask:
|
||||
flags |= FILE_LOAD_SEQUENCE_ASK;
|
||||
break;
|
||||
case SequenceDecision::Agree:
|
||||
flags |= FILE_LOAD_SEQUENCE_YES;
|
||||
break;
|
||||
case SequenceDecision::Skip:
|
||||
flags |= FILE_LOAD_SEQUENCE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
base::UniquePtr<FileOp> fop(
|
||||
FileOp::createLoadDocumentOperation(
|
||||
context, m_filename.c_str(), FILE_LOAD_SEQUENCE_ASK));
|
||||
context, m_filename.c_str(), flags));
|
||||
bool unrecent = false;
|
||||
|
||||
if (fop) {
|
||||
@ -129,6 +135,21 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
unrecent = true;
|
||||
}
|
||||
else {
|
||||
if (fop->isSequence()) {
|
||||
|
||||
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
|
||||
m_seqDecision = SequenceDecision::Agree;
|
||||
}
|
||||
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
|
||||
m_seqDecision = SequenceDecision::Skip;
|
||||
}
|
||||
|
||||
m_usedFiles = fop->filenames();
|
||||
}
|
||||
else {
|
||||
m_usedFiles.push_back(fop->filename());
|
||||
}
|
||||
|
||||
OpenFileJob task(fop);
|
||||
task.showProgressWindow();
|
||||
|
||||
@ -136,7 +157,7 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
fop->postLoad();
|
||||
|
||||
// Show any error
|
||||
if (fop->hasError())
|
||||
if (fop->hasError() && !fop->isStop())
|
||||
console.printf(fop->error().c_str());
|
||||
|
||||
Document* document = fop->document();
|
||||
|
53
src/app/commands/cmd_open_file.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_COMMANDS_CMD_OPEN_FILE_H_INCLUDED
|
||||
#define APP_COMMANDS_CMD_OPEN_FILE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/commands/command.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
|
||||
class OpenFileCommand : public Command {
|
||||
public:
|
||||
enum class SequenceDecision {
|
||||
Ask, Agree, Skip,
|
||||
};
|
||||
|
||||
OpenFileCommand();
|
||||
Command* clone() const override { return new OpenFileCommand(*this); }
|
||||
|
||||
SequenceDecision sequenceDecision() const {
|
||||
return m_seqDecision;
|
||||
}
|
||||
|
||||
void setSequenceDecision(SequenceDecision seqDecision) {
|
||||
m_seqDecision = seqDecision;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& usedFiles() const {
|
||||
return m_usedFiles;
|
||||
}
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
||||
private:
|
||||
std::string m_filename;
|
||||
std::string m_folder;
|
||||
bool m_repeatCheckbox;
|
||||
std::vector<std::string> m_usedFiles;
|
||||
SequenceDecision m_seqDecision;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -109,6 +109,8 @@ public:
|
||||
if (m_pref.general.rewindOnStop())
|
||||
rewindOnStop()->setSelected(true);
|
||||
|
||||
firstFrame()->setTextf("%d", m_globPref.timeline.firstFrame());
|
||||
|
||||
if (m_pref.general.expandMenubarOnMouseover())
|
||||
expandMenubarOnMouseover()->setSelected(true);
|
||||
|
||||
@ -158,9 +160,9 @@ public:
|
||||
showScrollbars()->setSelected(true);
|
||||
|
||||
// Scope
|
||||
gridScope()->addItem("Global");
|
||||
gridScope()->addItem("New Documents");
|
||||
if (context->activeDocument()) {
|
||||
gridScope()->addItem("Current Document");
|
||||
gridScope()->addItem("Active Document");
|
||||
gridScope()->setSelectedItemIndex(1);
|
||||
gridScope()->Change.connect(base::Bind<void>(&OptionsWindow::onChangeGridScope, this));
|
||||
}
|
||||
@ -244,6 +246,7 @@ public:
|
||||
void saveConfig() {
|
||||
m_pref.general.autoshowTimeline(autotimeline()->isSelected());
|
||||
m_pref.general.rewindOnStop(rewindOnStop()->isSelected());
|
||||
m_globPref.timeline.firstFrame(firstFrame()->textInt());
|
||||
m_pref.general.showFullPath(showFullPath()->isSelected());
|
||||
|
||||
bool expandOnMouseover = expandMenubarOnMouseover()->isSelected();
|
||||
@ -252,7 +255,7 @@ public:
|
||||
|
||||
std::string warnings;
|
||||
|
||||
int newPeriod = base::convert_to<int>(dataRecoveryPeriod()->getValue());
|
||||
double newPeriod = base::convert_to<double>(dataRecoveryPeriod()->getValue());
|
||||
if (enableDataRecovery()->isSelected() != m_pref.general.dataRecovery() ||
|
||||
newPeriod != m_pref.general.dataRecoveryPeriod()) {
|
||||
m_pref.general.dataRecovery(enableDataRecovery()->isSelected());
|
||||
|
@ -251,10 +251,10 @@ PaletteEntryEditor::PaletteEntryEditor()
|
||||
, m_selfPalChange(false)
|
||||
, m_fromPalette(0, 0)
|
||||
{
|
||||
m_colorType.addItem("RGB");
|
||||
m_colorType.addItem("HSB");
|
||||
m_changeMode.addItem("Abs");
|
||||
m_changeMode.addItem("Rel");
|
||||
m_colorType.addItem("RGB")->setFocusStop(false);
|
||||
m_colorType.addItem("HSB")->setFocusStop(false);
|
||||
m_changeMode.addItem("Abs")->setFocusStop(false);
|
||||
m_changeMode.addItem("Rel")->setFocusStop(false);
|
||||
|
||||
m_topBox.setBorder(gfx::Border(0));
|
||||
m_topBox.setChildSpacing(0);
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/console.h"
|
||||
|
@ -29,7 +29,6 @@ FOR_EACH_COMMAND(CropSprite)
|
||||
FOR_EACH_COMMAND(Cut)
|
||||
FOR_EACH_COMMAND(DeselectMask)
|
||||
FOR_EACH_COMMAND(Despeckle)
|
||||
FOR_EACH_COMMAND(DeveloperConsole)
|
||||
FOR_EACH_COMMAND(DiscardBrush)
|
||||
FOR_EACH_COMMAND(DuplicateLayer)
|
||||
FOR_EACH_COMMAND(DuplicateSprite)
|
||||
@ -100,7 +99,6 @@ FOR_EACH_COMMAND(ReplaceColor)
|
||||
FOR_EACH_COMMAND(ReselectMask)
|
||||
FOR_EACH_COMMAND(ReverseFrames)
|
||||
FOR_EACH_COMMAND(Rotate)
|
||||
FOR_EACH_COMMAND(RunScript)
|
||||
FOR_EACH_COMMAND(SaveFile)
|
||||
FOR_EACH_COMMAND(SaveFileAs)
|
||||
FOR_EACH_COMMAND(SaveFileCopyAs)
|
||||
@ -136,3 +134,8 @@ FOR_EACH_COMMAND(Undo)
|
||||
FOR_EACH_COMMAND(UndoHistory)
|
||||
FOR_EACH_COMMAND(UnlinkCel)
|
||||
FOR_EACH_COMMAND(Zoom)
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
FOR_EACH_COMMAND(DeveloperConsole)
|
||||
FOR_EACH_COMMAND(RunScript)
|
||||
#endif
|
||||
|
@ -8,11 +8,13 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory.h"
|
||||
#include "base/string.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include "app/app.h"
|
||||
@ -106,15 +108,13 @@ Console::~Console()
|
||||
|
||||
void Console::printf(const char* format, ...)
|
||||
{
|
||||
char buf[4096]; // TODO warning buffer overflow
|
||||
va_list ap;
|
||||
|
||||
std::va_list ap;
|
||||
va_start(ap, format);
|
||||
vsprintf(buf, format, ap);
|
||||
std::string msg = base::string_vprintf(format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!m_withUI || !wid_console) {
|
||||
fputs(buf, stdout);
|
||||
fputs(msg.c_str(), stdout);
|
||||
fflush(stdout);
|
||||
return;
|
||||
}
|
||||
@ -125,7 +125,7 @@ void Console::printf(const char* format, ...)
|
||||
ui::Manager::getDefault()->invalidate();
|
||||
}
|
||||
|
||||
/* update the textbox */
|
||||
// Update the textbox
|
||||
if (!console_locked) {
|
||||
console_locked = true;
|
||||
|
||||
@ -142,7 +142,7 @@ void Console::printf(const char* format, ...)
|
||||
std::string final;
|
||||
if (!text.empty())
|
||||
final += text;
|
||||
final += buf;
|
||||
final += msg;
|
||||
|
||||
wid_textbox->setText(final.c_str());
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ void BackupObserver::onRemoveDocument(doc::Document* document)
|
||||
|
||||
void BackupObserver::backgroundThread()
|
||||
{
|
||||
int normalPeriod = 60*Preferences::instance().general.dataRecoveryPeriod();
|
||||
int lockedPeriod = 10;
|
||||
int normalPeriod = int(60.0*Preferences::instance().general.dataRecoveryPeriod());
|
||||
int lockedPeriod = 5;
|
||||
#if 0 // Just for testing purposes
|
||||
normalPeriod = 5;
|
||||
lockedPeriod = 5;
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include "render/render.h"
|
||||
#include "ui/alert.h"
|
||||
|
||||
#include "open_sequence.xml.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
|
||||
@ -191,6 +193,7 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filena
|
||||
if (fop->m_format->support(FILE_SUPPORT_SEQUENCES)) {
|
||||
/* prepare to load a sequence */
|
||||
fop->prepareForSequence();
|
||||
fop->m_seq.flags = flags;
|
||||
|
||||
/* per now, we want load just one file */
|
||||
fop->m_seq.filename_list.push_back(filename);
|
||||
@ -221,18 +224,57 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filena
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO add a better dialog to edit file-names */
|
||||
if ((flags & FILE_LOAD_SEQUENCE_ASK) && context && context->isUIAvailable()) {
|
||||
/* really want load all files? */
|
||||
if ((fop->m_seq.filename_list.size() > 1) &&
|
||||
(ui::Alert::show("Notice"
|
||||
"<<Possible animation with:"
|
||||
"<<%s, %s..."
|
||||
"<<Do you want to load the sequence of bitmaps?"
|
||||
"||&Agree||&Skip",
|
||||
base::get_file_name(fop->m_seq.filename_list[0]).c_str(),
|
||||
base::get_file_name(fop->m_seq.filename_list[1]).c_str()) != 1)) {
|
||||
// TODO add a better dialog to edit file-names
|
||||
if ((flags & FILE_LOAD_SEQUENCE_ASK) &&
|
||||
context &&
|
||||
context->isUIAvailable() &&
|
||||
fop->m_seq.filename_list.size() > 1) {
|
||||
app::gen::OpenSequence window;
|
||||
window.repeat()->setVisible(flags & FILE_LOAD_SEQUENCE_ASK_CHECKBOX ? true: false);
|
||||
|
||||
for (const auto& fn : fop->m_seq.filename_list) {
|
||||
auto item = new ui::ListItem(base::get_file_name(fn));
|
||||
item->setSelected(true);
|
||||
window.files()->addChild(item);
|
||||
}
|
||||
|
||||
window.files()->Change.connect(
|
||||
[&window]{
|
||||
window.agree()->setEnabled(
|
||||
window.files()->getSelectedChild() != nullptr);
|
||||
});
|
||||
|
||||
window.openWindowInForeground();
|
||||
|
||||
// If the user selected the "do the same for other files"
|
||||
// checkbox, we've to save what the user want to do for the
|
||||
// following files.
|
||||
if (window.repeat()->isSelected()) {
|
||||
if (window.closer() == window.agree())
|
||||
fop->m_seq.flags = FILE_LOAD_SEQUENCE_YES;
|
||||
else
|
||||
fop->m_seq.flags = FILE_LOAD_SEQUENCE_NONE;
|
||||
}
|
||||
|
||||
if (window.closer() == window.agree()) {
|
||||
// If the user replies "Agree", we load the selected files.
|
||||
std::vector<std::string> list;
|
||||
|
||||
auto it = window.files()->children().begin();
|
||||
auto end = window.files()->children().end();
|
||||
for (const auto& fn : fop->m_seq.filename_list) {
|
||||
ASSERT(it != end);
|
||||
if (it == end)
|
||||
break;
|
||||
if ((*it)->isSelected())
|
||||
list.push_back(fn);
|
||||
++it;
|
||||
}
|
||||
|
||||
ASSERT(!list.empty());
|
||||
fop->m_seq.filename_list = list;
|
||||
}
|
||||
else {
|
||||
// If the user replies "Skip", we need just one file name
|
||||
// (the first one).
|
||||
if (fop->m_seq.filename_list.size() > 1) {
|
||||
@ -944,6 +986,7 @@ FileOp::FileOp(FileOpType type, Context* context)
|
||||
m_seq.frame = frame_t(0);
|
||||
m_seq.layer = nullptr;
|
||||
m_seq.last_cel = nullptr;
|
||||
m_seq.flags = 0;
|
||||
}
|
||||
|
||||
void FileOp::prepareForSequence()
|
||||
|
@ -22,8 +22,9 @@
|
||||
// Flags for FileOp::createLoadDocumentOperation()
|
||||
#define FILE_LOAD_SEQUENCE_NONE 0x00000001
|
||||
#define FILE_LOAD_SEQUENCE_ASK 0x00000002
|
||||
#define FILE_LOAD_SEQUENCE_YES 0x00000004
|
||||
#define FILE_LOAD_ONE_FRAME 0x00000008
|
||||
#define FILE_LOAD_SEQUENCE_ASK_CHECKBOX 0x00000004
|
||||
#define FILE_LOAD_SEQUENCE_YES 0x00000008
|
||||
#define FILE_LOAD_ONE_FRAME 0x00000010
|
||||
|
||||
namespace doc {
|
||||
class Document;
|
||||
@ -84,6 +85,11 @@ namespace app {
|
||||
};
|
||||
|
||||
// Structure to load & save files.
|
||||
//
|
||||
// TODO This class do to many things. There should be a previous
|
||||
// instance (class) to verify what the user want to do with the
|
||||
// sequence of files, and the result of that operation should be the
|
||||
// input of this one.
|
||||
class FileOp {
|
||||
public:
|
||||
static FileOp* createLoadDocumentOperation(Context* context,
|
||||
@ -101,6 +107,7 @@ namespace app {
|
||||
bool isOneFrame() const { return m_oneframe; }
|
||||
|
||||
const std::string& filename() const { return m_filename; }
|
||||
const std::vector<std::string>& filenames() const { return m_seq.filename_list; }
|
||||
Context* context() const { return m_context; }
|
||||
Document* document() const { return m_document; }
|
||||
Document* releaseDocument() {
|
||||
@ -142,6 +149,9 @@ namespace app {
|
||||
void sequenceSetHasAlpha(bool hasAlpha) {
|
||||
m_seq.has_alpha = hasAlpha;
|
||||
}
|
||||
int sequenceFlags() const {
|
||||
return m_seq.flags;
|
||||
}
|
||||
|
||||
const std::string& error() const { return m_error; }
|
||||
void setError(const char *error, ...);
|
||||
@ -191,6 +201,8 @@ namespace app {
|
||||
bool has_alpha;
|
||||
LayerImage* layer;
|
||||
Cel* last_cel;
|
||||
// Flags after the user choose what to do with the sequence.
|
||||
int flags;
|
||||
} m_seq;
|
||||
|
||||
void prepareForSequence();
|
||||
|
@ -19,10 +19,19 @@
|
||||
#include "ui/widget.h"
|
||||
#include "ui/window.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
static const int kMonitoringPeriod = 100;
|
||||
static std::atomic<int> g_runningJobs(0);
|
||||
|
||||
namespace app {
|
||||
|
||||
// static
|
||||
int Job::runningJobs()
|
||||
{
|
||||
return g_runningJobs;
|
||||
}
|
||||
|
||||
Job::Job(const char* jobName)
|
||||
{
|
||||
m_mutex = NULL;
|
||||
@ -60,6 +69,7 @@ Job::~Job()
|
||||
void Job::startJob()
|
||||
{
|
||||
m_thread = new base::thread(&Job::thread_proc, this);
|
||||
++g_runningJobs;
|
||||
|
||||
if (m_alert_window) {
|
||||
m_alert_window->openWindowInForeground();
|
||||
@ -93,7 +103,9 @@ void Job::waitJob()
|
||||
if (m_thread) {
|
||||
m_thread->join();
|
||||
delete m_thread;
|
||||
m_thread = NULL;
|
||||
m_thread = nullptr;
|
||||
|
||||
--g_runningJobs;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ namespace app {
|
||||
|
||||
class Job {
|
||||
public:
|
||||
static int runningJobs();
|
||||
|
||||
Job(const char* jobName);
|
||||
virtual ~Job();
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/cmd_open_file.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
@ -345,16 +346,14 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
|
||||
case kDropFilesMessage:
|
||||
{
|
||||
const DropFilesMessage::Files& files = static_cast<DropFilesMessage*>(msg)->files();
|
||||
|
||||
// Open all files
|
||||
Command* cmd_open_file =
|
||||
CommandsModule::instance()->getCommandByName(CommandId::OpenFile);
|
||||
Params params;
|
||||
|
||||
DropFilesMessage::Files files = static_cast<DropFilesMessage*>(msg)->files();
|
||||
UIContext* ctx = UIContext::instance();
|
||||
OpenFileCommand cmd;
|
||||
|
||||
while (!files.empty()) {
|
||||
auto fn = files.front();
|
||||
files.erase(files.begin());
|
||||
|
||||
for (const auto& fn : files) {
|
||||
// If the document is already open, select it.
|
||||
Document* doc = static_cast<Document*>(ctx->documents().getByFileName(fn));
|
||||
if (doc) {
|
||||
@ -367,8 +366,17 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
}
|
||||
// Load the file
|
||||
else {
|
||||
Params params;
|
||||
params.set("filename", fn.c_str());
|
||||
ctx->executeCommand(cmd_open_file, params);
|
||||
params.set("repeat_checkbox", "true");
|
||||
ctx->executeCommand(&cmd, params);
|
||||
|
||||
// Remove all used file names from the "dropped files"
|
||||
for (const auto& usedFn : cmd.usedFiles()) {
|
||||
auto it = std::find(files.begin(), files.end(), usedFn);
|
||||
if (it != files.end())
|
||||
files.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,10 +384,12 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
|
||||
case kKeyDownMessage: {
|
||||
#ifdef _DEBUG
|
||||
auto keymsg = static_cast<KeyMessage*>(msg);
|
||||
|
||||
// Ctrl+Shift+Q generates a crash (useful to test the anticrash feature)
|
||||
if (msg->ctrlPressed() &&
|
||||
msg->shiftPressed() &&
|
||||
static_cast<KeyMessage*>(msg)->scancode() == kKeyQ) {
|
||||
keymsg->scancode() == kKeyQ) {
|
||||
int* p = nullptr;
|
||||
*p = 0;
|
||||
}
|
||||
@ -388,7 +398,7 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
// Ctrl+Shift+R recover active sprite from the backup store
|
||||
if (msg->ctrlPressed() &&
|
||||
msg->shiftPressed() &&
|
||||
static_cast<KeyMessage*>(msg)->scancode() == kKeyR &&
|
||||
keymsg->scancode() == kKeyR &&
|
||||
App::instance()->dataRecovery() &&
|
||||
App::instance()->dataRecovery()->activeSession() &&
|
||||
current_editor &&
|
||||
|
@ -8,6 +8,10 @@
|
||||
#define APP_SCRIPTING_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
#include "doc/object_id.h"
|
||||
#include "script/engine.h"
|
||||
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
#include "app/shell.h"
|
||||
|
||||
#include "script/engine.h"
|
||||
|
@ -8,6 +8,10 @@
|
||||
#define APP_SHELL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
namespace script {
|
||||
class Engine;
|
||||
}
|
||||
|
@ -161,10 +161,8 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
|
||||
case ui::kKeyDownMessage:
|
||||
if (isEnabled() && hasText()) {
|
||||
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
|
||||
bool mnemonicPressed =
|
||||
(msg->altPressed() &&
|
||||
mnemonicChar() &&
|
||||
mnemonicChar() == tolower(keymsg->unicodeChar()));
|
||||
bool mnemonicPressed = (msg->altPressed() &&
|
||||
mnemonicCharPressed(keymsg));
|
||||
|
||||
if (mnemonicPressed ||
|
||||
(hasFocus() && keymsg->scancode() == kKeySpace)) {
|
||||
|
@ -58,11 +58,11 @@ ColorPopup::ColorPopup(bool canPin)
|
||||
, m_canPin(canPin)
|
||||
, m_disableHexUpdate(false)
|
||||
{
|
||||
m_colorType.addItem("Index");
|
||||
m_colorType.addItem("RGB");
|
||||
m_colorType.addItem("HSB");
|
||||
m_colorType.addItem("Gray");
|
||||
m_colorType.addItem("Mask");
|
||||
m_colorType.addItem("Index")->setFocusStop(false);
|
||||
m_colorType.addItem("RGB")->setFocusStop(false);
|
||||
m_colorType.addItem("HSB")->setFocusStop(false);
|
||||
m_colorType.addItem("Gray")->setFocusStop(false);
|
||||
m_colorType.addItem("Mask")->setFocusStop(false);
|
||||
|
||||
m_topBox.setBorder(gfx::Border(0));
|
||||
m_topBox.setChildSpacing(0);
|
||||
@ -260,6 +260,9 @@ void ColorPopup::selectColorType(app::Color::Type type)
|
||||
case app::Color::MaskType: m_colorType.setSelectedItem(MASK_MODE); break;
|
||||
}
|
||||
|
||||
// Remove focus from some RGB/HSB text entry
|
||||
manager()->freeFocus();
|
||||
|
||||
m_vbox.layout();
|
||||
m_vbox.invalidate();
|
||||
}
|
||||
|
@ -13,10 +13,12 @@
|
||||
#include "app/ui/skin/skin_slider_property.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/scoped_value.h"
|
||||
#include "ui/box.h"
|
||||
#include "ui/entry.h"
|
||||
#include "ui/graphics.h"
|
||||
#include "ui/label.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/size_hint_event.h"
|
||||
#include "ui/slider.h"
|
||||
#include "ui/theme.h"
|
||||
@ -81,6 +83,104 @@ namespace {
|
||||
app::Color m_color;
|
||||
};
|
||||
|
||||
class ColorEntry : public Entry {
|
||||
public:
|
||||
ColorEntry(Slider* absSlider, Slider* relSlider)
|
||||
: Entry(4, "0")
|
||||
, m_absSlider(absSlider)
|
||||
, m_relSlider(relSlider)
|
||||
, m_recent_focus(false) {
|
||||
}
|
||||
|
||||
private:
|
||||
int minValue() const {
|
||||
if (m_absSlider->isVisible())
|
||||
return m_absSlider->getMinValue();
|
||||
else if (m_relSlider->isVisible())
|
||||
return m_relSlider->getMinValue();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxValue() const {
|
||||
if (m_absSlider->isVisible())
|
||||
return m_absSlider->getMaxValue();
|
||||
else if (m_relSlider->isVisible())
|
||||
return m_relSlider->getMaxValue();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool onProcessMessage(Message* msg) override {
|
||||
switch (msg->type()) {
|
||||
|
||||
case kFocusEnterMessage:
|
||||
m_recent_focus = true;
|
||||
break;
|
||||
|
||||
case kKeyDownMessage:
|
||||
if (Entry::onProcessMessage(msg))
|
||||
return true;
|
||||
|
||||
if (hasFocus()) {
|
||||
int scancode = static_cast<KeyMessage*>(msg)->scancode();
|
||||
|
||||
switch (scancode) {
|
||||
// Enter just remove the focus
|
||||
case kKeyEnter:
|
||||
case kKeyEnterPad:
|
||||
releaseFocus();
|
||||
return true;
|
||||
|
||||
case kKeyDown:
|
||||
case kKeyUp: {
|
||||
int value = textInt();
|
||||
if (scancode == kKeyDown)
|
||||
--value;
|
||||
else
|
||||
++value;
|
||||
|
||||
setTextf("%d", MID(minValue(), value, maxValue()));
|
||||
selectAllText();
|
||||
|
||||
onChange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process focus movement key here because if our
|
||||
// CustomizedGuiManager catches this kKeyDownMessage it
|
||||
// will process it as a shortcut to switch the Timeline.
|
||||
//
|
||||
// Note: The default ui::Manager handles focus movement
|
||||
// shortcuts only for foreground windows.
|
||||
// TODO maybe that should change
|
||||
if (hasFocus() &&
|
||||
manager()->processFocusMovementMessage(msg))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = Entry::onProcessMessage(msg);
|
||||
|
||||
if (msg->type() == kMouseDownMessage && m_recent_focus) {
|
||||
m_recent_focus = false;
|
||||
selectAllText();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Slider* m_absSlider;
|
||||
Slider* m_relSlider;
|
||||
|
||||
// TODO remove this calling setFocus() in
|
||||
// Widget::onProcessMessage() instead of
|
||||
// Manager::handleWindowZOrder()
|
||||
bool m_recent_focus;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -90,6 +190,7 @@ ColorSliders::ColorSliders()
|
||||
: Widget(kGenericWidget)
|
||||
, m_grid(3, false)
|
||||
, m_mode(Absolute)
|
||||
, m_lockEntry(-1)
|
||||
{
|
||||
addChild(&m_grid);
|
||||
m_grid.setChildSpacing(0);
|
||||
@ -136,7 +237,7 @@ void ColorSliders::addSlider(Channel channel, const char* labelText, int min, in
|
||||
Label* label = new Label(labelText);
|
||||
Slider* absSlider = new Slider(min, max, 0);
|
||||
Slider* relSlider = new Slider(min-max, max-min, 0);
|
||||
Entry* entry = new Entry(4, "0");
|
||||
Entry* entry = new ColorEntry(absSlider, relSlider);
|
||||
|
||||
m_label.push_back(label);
|
||||
m_absSlider.push_back(absSlider);
|
||||
@ -155,6 +256,8 @@ void ColorSliders::addSlider(Channel channel, const char* labelText, int min, in
|
||||
HBox* box = new HBox();
|
||||
box->addChild(absSlider);
|
||||
box->addChild(relSlider);
|
||||
absSlider->setFocusStop(false);
|
||||
relSlider->setFocusStop(false);
|
||||
absSlider->setExpansive(true);
|
||||
relSlider->setExpansive(true);
|
||||
relSlider->setVisible(false);
|
||||
@ -193,6 +296,8 @@ void ColorSliders::onSliderChange(int i)
|
||||
|
||||
void ColorSliders::onEntryChange(int i)
|
||||
{
|
||||
base::ScopedValue<int> lock(m_lockEntry, i, m_lockEntry);
|
||||
|
||||
// Update the slider related to the changed entry widget.
|
||||
int value = m_entry[i]->textInt();
|
||||
|
||||
@ -220,10 +325,15 @@ void ColorSliders::onControlChange(int i)
|
||||
// Updates the entry related to the changed slider widget.
|
||||
void ColorSliders::updateEntryText(int entryIndex)
|
||||
{
|
||||
if (m_lockEntry == entryIndex)
|
||||
return;
|
||||
|
||||
Slider* slider = (m_mode == Absolute ? m_absSlider[entryIndex]:
|
||||
m_relSlider[entryIndex]);
|
||||
|
||||
m_entry[entryIndex]->setTextf("%d", slider->getValue());
|
||||
if (m_entry[entryIndex]->hasFocus())
|
||||
m_entry[entryIndex]->selectAllText();
|
||||
}
|
||||
|
||||
void ColorSliders::updateSlidersBgColor(const app::Color& color)
|
||||
|
@ -72,6 +72,7 @@ namespace app {
|
||||
std::vector<Channel> m_channel;
|
||||
ui::Grid m_grid;
|
||||
Mode m_mode;
|
||||
int m_lockEntry;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -52,6 +52,7 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
|
||||
addChild(m_box);
|
||||
|
||||
m_box->position()->ItemChange.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangePosition, this));
|
||||
m_box->firstFrame()->Change.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeFirstFrame, this));
|
||||
m_box->merge()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, this));
|
||||
m_box->tint()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, this));
|
||||
m_box->opacity()->Change.connect(base::Bind<void>(&ConfigureTimelinePopup::onOpacity, this));
|
||||
@ -92,6 +93,9 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
|
||||
}
|
||||
m_box->position()->setSelectedItem(selItem, false);
|
||||
|
||||
m_box->firstFrame()->setTextf(
|
||||
"%d", docPref.timeline.firstFrame());
|
||||
|
||||
switch (docPref.onionskin.type()) {
|
||||
case app::gen::OnionskinType::MERGE:
|
||||
m_box->merge()->setSelected(true);
|
||||
@ -155,6 +159,12 @@ void ConfigureTimelinePopup::onChangePosition()
|
||||
Preferences::instance().general.timelinePosition(newTimelinePos);
|
||||
}
|
||||
|
||||
void ConfigureTimelinePopup::onChangeFirstFrame()
|
||||
{
|
||||
docPref().timeline.firstFrame(
|
||||
m_box->firstFrame()->textInt());
|
||||
}
|
||||
|
||||
void ConfigureTimelinePopup::onChangeType()
|
||||
{
|
||||
if (m_lockUpdates)
|
||||
|
@ -33,6 +33,7 @@ namespace app {
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
void onChangePosition();
|
||||
void onChangeFirstFrame();
|
||||
void onChangeType();
|
||||
void onOpacity();
|
||||
void onOpacityStep();
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
#include "app/ui/devconsole_view.h"
|
||||
|
||||
#include "app/app_menus.h"
|
||||
|
@ -8,6 +8,10 @@
|
||||
#define APP_UI_DEVCONSOLE_VIEW_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#ifndef ENABLE_SCRIPTING
|
||||
#error ENABLE_SCRIPTING must be defined
|
||||
#endif
|
||||
|
||||
#include "app/script/app_scripting.h"
|
||||
#include "app/ui/tabs.h"
|
||||
#include "app/ui/workspace_view.h"
|
||||
|
@ -1409,7 +1409,7 @@ bool Editor::onProcessMessage(Message* msg)
|
||||
case kFocusLeaveMessage:
|
||||
// As we use keys like Space-bar as modifier, we can clear the
|
||||
// keyboard buffer when we lost the focus.
|
||||
she::clear_keyboard_buffer();
|
||||
she::instance()->clearKeyboardBuffer();
|
||||
break;
|
||||
|
||||
case kMouseWheelMessage:
|
||||
|
@ -232,7 +232,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
MovingCelState* newState = new MovingCelState(editor, msg, handle);
|
||||
editor->setState(EditorStatePtr(newState));
|
||||
}
|
||||
catch (const LockedDocumentException& ex) {
|
||||
catch (const LockedDocumentException&) {
|
||||
// TODO break the background task that is locking this sprite
|
||||
StatusBar::instance()->showTip(1000,
|
||||
"Sprite is used by a backup/data recovery task");
|
||||
@ -502,7 +502,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
|
||||
if (sprite->totalFrames() > 1) {
|
||||
sprintf(
|
||||
buf+std::strlen(buf), " :frame: %d :clock: %d",
|
||||
editor->frame()+1,
|
||||
editor->frame()+editor->docPref().timeline.firstFrame(),
|
||||
sprite->frameDuration(editor->frame()));
|
||||
}
|
||||
|
||||
|
@ -113,15 +113,18 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
|
||||
}
|
||||
break;
|
||||
|
||||
case WHEEL_FRAME:
|
||||
{
|
||||
Command* command = CommandsModule::instance()->getCommandByName
|
||||
((dz < 0.0) ? CommandId::GotoNextFrame:
|
||||
CommandId::GotoPreviousFrame);
|
||||
if (command)
|
||||
UIContext::instance()->executeCommand(command);
|
||||
}
|
||||
case WHEEL_FRAME: {
|
||||
Command* command = nullptr;
|
||||
|
||||
if (dz < 0.0)
|
||||
command = CommandsModule::instance()->getCommandByName(CommandId::GotoNextFrame);
|
||||
else if (dz > 0.0)
|
||||
command = CommandsModule::instance()->getCommandByName(CommandId::GotoPreviousFrame);
|
||||
|
||||
if (command)
|
||||
UIContext::instance()->executeCommand(command);
|
||||
break;
|
||||
}
|
||||
|
||||
case WHEEL_ZOOM: {
|
||||
render::Zoom zoom = editor->zoom();
|
||||
|
@ -93,8 +93,6 @@ void FileList::setCurrentFolder(IFileItem* folder)
|
||||
|
||||
invalidate();
|
||||
View::getView(this)->updateView();
|
||||
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
void FileList::goUp()
|
||||
@ -279,7 +277,12 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
View* view = View::getView(this);
|
||||
if (view) {
|
||||
gfx::Point scroll = view->viewScroll();
|
||||
scroll += static_cast<MouseMessage*>(msg)->wheelDelta() * 3*(textHeight()+4*guiscale());
|
||||
|
||||
if (static_cast<MouseMessage*>(msg)->preciseWheel())
|
||||
scroll += static_cast<MouseMessage*>(msg)->wheelDelta();
|
||||
else
|
||||
scroll += static_cast<MouseMessage*>(msg)->wheelDelta() * 3*(textHeight()+4*guiscale());
|
||||
|
||||
view->setViewScroll(scroll);
|
||||
}
|
||||
break;
|
||||
|
@ -196,8 +196,8 @@ protected:
|
||||
int unicode = keyMsg->unicodeChar();
|
||||
bool up = (msg->cmdPressed() && scancode == kKeyUp);
|
||||
bool enter = (msg->cmdPressed() && scancode == kKeyDown);
|
||||
bool back = (msg->cmdPressed() && msg->shiftPressed() && unicode == '[');
|
||||
bool forward = (msg->cmdPressed() && msg->shiftPressed() && unicode == ']');
|
||||
bool back = (msg->cmdPressed() && (unicode == '[' || scancode == kKeyOpenbrace));
|
||||
bool forward = (msg->cmdPressed() && (unicode == ']' || scancode == kKeyClosebrace));
|
||||
#else
|
||||
bool up = (msg->altPressed() && scancode == kKeyUp);
|
||||
bool enter = (msg->altPressed() && scancode == kKeyDown);
|
||||
@ -851,12 +851,12 @@ void FileSelector::onLocationCloseListBox()
|
||||
}
|
||||
}
|
||||
|
||||
if (fileItem != NULL) {
|
||||
if (fileItem) {
|
||||
m_fileList->setCurrentFolder(fileItem);
|
||||
|
||||
// Refocus the 'fileview' (the focus in that widget is more
|
||||
// useful for the user)
|
||||
manager()->setFocus(m_fileList);
|
||||
m_fileList->requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,21 +14,41 @@
|
||||
#include "app/ui/hex_color_entry.h"
|
||||
#include "base/hex.h"
|
||||
#include "gfx/border.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/theme.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
HexColorEntry::CustomEntry::CustomEntry()
|
||||
: Entry(16, "")
|
||||
{
|
||||
}
|
||||
|
||||
bool HexColorEntry::CustomEntry::onProcessMessage(ui::Message* msg)
|
||||
{
|
||||
switch (msg->type()) {
|
||||
case kMouseDownMessage:
|
||||
setFocusStop(true);
|
||||
requestFocus();
|
||||
break;
|
||||
case kFocusLeaveMessage:
|
||||
setFocusStop(false);
|
||||
break;
|
||||
}
|
||||
return Entry::onProcessMessage(msg);
|
||||
}
|
||||
|
||||
HexColorEntry::HexColorEntry()
|
||||
: Box(HORIZONTAL)
|
||||
, m_label("#")
|
||||
, m_entry(16, "")
|
||||
{
|
||||
addChild(&m_label);
|
||||
addChild(&m_entry);
|
||||
|
||||
m_entry.Change.connect(&HexColorEntry::onEntryChange, this);
|
||||
m_entry.setFocusStop(false);
|
||||
|
||||
initTheme();
|
||||
|
||||
|
@ -30,8 +30,15 @@ namespace app {
|
||||
void onEntryChange();
|
||||
|
||||
private:
|
||||
class CustomEntry : public ui::Entry {
|
||||
public:
|
||||
CustomEntry();
|
||||
private:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
};
|
||||
|
||||
ui::Label m_label;
|
||||
ui::Entry m_entry;
|
||||
CustomEntry m_entry;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/devconsole_view.h"
|
||||
#include "app/ui/document_view.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/editor/editor_view.h"
|
||||
@ -43,6 +42,10 @@
|
||||
#include "ui/system.h"
|
||||
#include "ui/view.h"
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "app/ui/devconsole_view.h"
|
||||
#endif
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
@ -90,8 +93,10 @@ public:
|
||||
MainWindow::MainWindow()
|
||||
: m_mode(NormalMode)
|
||||
, m_homeView(nullptr)
|
||||
, m_devConsoleView(nullptr)
|
||||
, m_scalePanic(nullptr)
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
, m_devConsoleView(nullptr)
|
||||
#endif
|
||||
{
|
||||
// Load all menus by first time.
|
||||
AppMenus::instance()->reload();
|
||||
@ -154,11 +159,14 @@ MainWindow::~MainWindow()
|
||||
{
|
||||
delete m_scalePanic;
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (m_devConsoleView) {
|
||||
if (m_devConsoleView->parent())
|
||||
m_workspace->removeView(m_devConsoleView);
|
||||
delete m_devConsoleView;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_homeView) {
|
||||
if (m_homeView->parent())
|
||||
m_workspace->removeView(m_homeView);
|
||||
@ -241,6 +249,7 @@ bool MainWindow::isHomeSelected()
|
||||
|
||||
void MainWindow::showDevConsole()
|
||||
{
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (!m_devConsoleView)
|
||||
m_devConsoleView = new DevConsoleView;
|
||||
|
||||
@ -248,6 +257,7 @@ void MainWindow::showDevConsole()
|
||||
m_workspace->addView(m_devConsoleView);
|
||||
m_tabsBar->selectTab(m_devConsoleView);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::setMode(Mode mode)
|
||||
|
@ -114,9 +114,11 @@ namespace app {
|
||||
Workspace* m_workspace;
|
||||
PreviewEditorWindow* m_previewEditor;
|
||||
HomeView* m_homeView;
|
||||
DevConsoleView* m_devConsoleView;
|
||||
Notifications* m_notifications;
|
||||
INotificationDelegate* m_scalePanic;
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
DevConsoleView* m_devConsoleView;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -34,9 +34,11 @@ PalettePopup::PalettePopup()
|
||||
{
|
||||
setAutoRemap(false);
|
||||
setBorder(gfx::Border(4*guiscale()));
|
||||
setEnterBehavior(EnterBehavior::DoNothingOnEnter);
|
||||
|
||||
addChild(m_popup);
|
||||
|
||||
m_paletteListBox.DoubleClickItem.connect(base::Bind<void>(&PalettePopup::onLoadPal, this));
|
||||
m_popup->loadPal()->Click.connect(base::Bind<void>(&PalettePopup::onLoadPal, this));
|
||||
m_popup->openFolder()->Click.connect(base::Bind<void>(&PalettePopup::onOpenFolder, this));
|
||||
|
||||
@ -75,6 +77,8 @@ void PalettePopup::onLoadPal()
|
||||
CommandsModule::instance()->getCommandByName(CommandId::SetPalette));
|
||||
cmd->setPalette(palette);
|
||||
UIContext::instance()->executeCommand(cmd);
|
||||
|
||||
m_paletteListBox.requestFocus();
|
||||
}
|
||||
|
||||
void PalettePopup::onOpenFolder()
|
||||
|
@ -186,15 +186,15 @@ app::Color PaletteView::getColorByPosition(const gfx::Point& pos)
|
||||
|
||||
int PaletteView::getBoxSize() const
|
||||
{
|
||||
return m_boxsize / guiscale();
|
||||
return int(m_boxsize) / guiscale();
|
||||
}
|
||||
|
||||
void PaletteView::setBoxSize(int boxsize)
|
||||
void PaletteView::setBoxSize(double boxsize)
|
||||
{
|
||||
m_boxsize = MID(4, boxsize, 32)*guiscale();
|
||||
m_boxsize = MID(4.0, boxsize, 32.0)*guiscale();
|
||||
|
||||
if (m_delegate)
|
||||
m_delegate->onPaletteViewChangeSize(m_boxsize / guiscale());
|
||||
m_delegate->onPaletteViewChangeSize(int(m_boxsize) / guiscale());
|
||||
|
||||
View* view = View::getView(this);
|
||||
if (view)
|
||||
@ -382,13 +382,19 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
|
||||
gfx::Point delta = static_cast<MouseMessage*>(msg)->wheelDelta();
|
||||
|
||||
if (msg->onlyCtrlPressed()) {
|
||||
if (msg->onlyCtrlPressed() ||
|
||||
msg->onlyCmdPressed()) {
|
||||
int z = delta.x - delta.y;
|
||||
setBoxSize(m_boxsize + z);
|
||||
}
|
||||
else {
|
||||
gfx::Point scroll = view->viewScroll();
|
||||
scroll += delta * 3 * m_boxsize;
|
||||
|
||||
if (static_cast<MouseMessage*>(msg)->preciseWheel())
|
||||
scroll += delta;
|
||||
else
|
||||
scroll += delta * 3 * m_boxsize;
|
||||
|
||||
view->setViewScroll(scroll);
|
||||
}
|
||||
break;
|
||||
@ -411,6 +417,11 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
case kTouchMagnifyMessage: {
|
||||
setBoxSize(m_boxsize + m_boxsize * static_cast<ui::TouchMessage*>(msg)->magnification());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Widget::onProcessMessage(msg);
|
||||
@ -475,14 +486,14 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
|
||||
case FgBgColors:
|
||||
if (fgIndex == i) {
|
||||
gfx::Color neg = color_utils::blackandwhite_neg(gfxColor);
|
||||
for (int i=0; i<m_boxsize/2; ++i)
|
||||
for (int i=0; i<int(m_boxsize/2); ++i)
|
||||
g->drawHLine(neg, box.x, box.y+i, m_boxsize/2-i);
|
||||
}
|
||||
|
||||
if (bgIndex == i) {
|
||||
gfx::Color neg = color_utils::blackandwhite_neg(gfxColor);
|
||||
for (int i=0; i<m_boxsize/4; ++i)
|
||||
g->drawHLine(neg, box.x+box.w-(i+1), box.y+box.h-m_boxsize/4+i, i+1);
|
||||
for (int i=0; i<int(m_boxsize/4); ++i)
|
||||
g->drawHLine(neg, box.x+box.w-(i+1), box.y+box.h-int(m_boxsize/4)+i, i+1);
|
||||
}
|
||||
|
||||
if (transparentIndex == i)
|
||||
@ -582,7 +593,7 @@ void PaletteView::onResize(ui::ResizeEvent& ev)
|
||||
if (view) {
|
||||
int columns =
|
||||
(view->viewportBounds().w-this->childSpacing()*2)
|
||||
/ (m_boxsize+this->childSpacing());
|
||||
/ (int(m_boxsize)+this->childSpacing());
|
||||
setColumns(MAX(1, columns));
|
||||
}
|
||||
m_isUpdatingColumns = false;
|
||||
@ -602,8 +613,8 @@ void PaletteView::onSizeHint(ui::SizeHintEvent& ev)
|
||||
}
|
||||
|
||||
gfx::Size sz;
|
||||
sz.w = border().width() + cols*m_boxsize + (cols-1)*childSpacing();
|
||||
sz.h = border().height() + rows*m_boxsize + (rows-1)*childSpacing();
|
||||
sz.w = border().width() + cols*int(m_boxsize) + (cols-1)*childSpacing();
|
||||
sz.h = border().height() + rows*int(m_boxsize) + (rows-1)*childSpacing();
|
||||
|
||||
ev.setSizeHint(sz);
|
||||
}
|
||||
@ -629,18 +640,18 @@ void PaletteView::update_scroll(int color)
|
||||
d = div(currentPalette()->size(), m_columns);
|
||||
cols = m_columns;
|
||||
|
||||
y = (m_boxsize+childSpacing()) * (color / cols);
|
||||
x = (m_boxsize+childSpacing()) * (color % cols);
|
||||
y = (int(m_boxsize)+childSpacing()) * (color / cols);
|
||||
x = (int(m_boxsize)+childSpacing()) * (color % cols);
|
||||
|
||||
if (scroll.x > x)
|
||||
scroll.x = x;
|
||||
else if (scroll.x+vp.w-m_boxsize-2 < x)
|
||||
scroll.x = x-vp.w+m_boxsize+2;
|
||||
else if (scroll.x+vp.w-int(m_boxsize)-2 < x)
|
||||
scroll.x = x-vp.w+int(m_boxsize)+2;
|
||||
|
||||
if (scroll.y > y)
|
||||
scroll.y = y;
|
||||
else if (scroll.y+vp.h-m_boxsize-2 < y)
|
||||
scroll.y = y-vp.h+m_boxsize+2;
|
||||
else if (scroll.y+vp.h-int(m_boxsize)-2 < y)
|
||||
scroll.y = y-vp.h+int(m_boxsize)+2;
|
||||
|
||||
view->setViewScroll(scroll);
|
||||
}
|
||||
@ -662,9 +673,9 @@ gfx::Rect PaletteView::getPaletteEntryBounds(int index) const
|
||||
int row = index / cols;
|
||||
|
||||
return gfx::Rect(
|
||||
bounds.x + border().left() + col*(m_boxsize+childSpacing()),
|
||||
bounds.y + border().top() + row*(m_boxsize+childSpacing()),
|
||||
m_boxsize, m_boxsize);
|
||||
bounds.x + border().left() + col*(int(m_boxsize)+childSpacing()),
|
||||
bounds.y + border().top() + row*(int(m_boxsize)+childSpacing()),
|
||||
int(m_boxsize), int(m_boxsize));
|
||||
}
|
||||
|
||||
PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
|
||||
@ -716,8 +727,8 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
|
||||
}
|
||||
|
||||
gfx::Rect box = getPaletteEntryBounds(0);
|
||||
box.w = (m_boxsize+childSpacing());
|
||||
box.h = (m_boxsize+childSpacing());
|
||||
box.w = (int(m_boxsize)+childSpacing());
|
||||
box.h = (int(m_boxsize)+childSpacing());
|
||||
|
||||
int colsLimit = m_columns;
|
||||
if (m_state == State::DRAGGING_OUTLINE)
|
||||
|
@ -74,7 +74,7 @@ namespace app {
|
||||
app::Color getColorByPosition(const gfx::Point& pos) override;
|
||||
|
||||
int getBoxSize() const;
|
||||
void setBoxSize(int boxsize);
|
||||
void setBoxSize(double boxsize);
|
||||
|
||||
void clearSelection();
|
||||
void cutToClipboard();
|
||||
@ -147,7 +147,7 @@ namespace app {
|
||||
PaletteViewStyle m_style;
|
||||
PaletteViewDelegate* m_delegate;
|
||||
int m_columns;
|
||||
int m_boxsize;
|
||||
double m_boxsize;
|
||||
int m_currentEntry;
|
||||
int m_rangeAnchor;
|
||||
doc::PalettePicks m_selectedEntries;
|
||||
|
@ -10,14 +10,19 @@
|
||||
|
||||
#include "app/ui/palettes_listbox.h"
|
||||
|
||||
#include "app/document.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/res/palette_resource.h"
|
||||
#include "app/res/palettes_loader_delegate.h"
|
||||
#include "app/ui/document_view.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/icon_button.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/launcher.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "she/surface.h"
|
||||
#include "ui/graphics.h"
|
||||
#include "ui/listitem.h"
|
||||
@ -63,7 +68,7 @@ class PalettesListItem : public ResourceListItem {
|
||||
|
||||
int j, i = m_comment.find("http");
|
||||
if (i != std::string::npos) {
|
||||
for (j=i+4; j<m_comment.size() && is_url_char(m_comment[j]); ++j)
|
||||
for (j=i+4; j<int(m_comment.size()) && is_url_char(m_comment[j]); ++j)
|
||||
;
|
||||
base::launcher::open_url(m_comment.substr(i, j-i));
|
||||
}
|
||||
@ -128,9 +133,22 @@ void PalettesListBox::onResourceChange(Resource* resource)
|
||||
PalChange(palette);
|
||||
}
|
||||
|
||||
void PalettesListBox::onPaintResource(Graphics* g, const gfx::Rect& bounds, Resource* resource)
|
||||
void PalettesListBox::onPaintResource(Graphics* g, gfx::Rect& bounds, Resource* resource)
|
||||
{
|
||||
doc::Palette* palette = static_cast<PaletteResource*>(resource)->palette();
|
||||
auto tick = SkinTheme::instance()->parts.checkSelected()->bitmap(0);
|
||||
|
||||
// Draw tick (to say "this palette matches the active sprite
|
||||
// palette").
|
||||
auto view = UIContext::instance()->activeView();
|
||||
if (view && view->document()) {
|
||||
auto docPal = view->document()->sprite()->palette(view->editor()->frame());
|
||||
if (docPal && *docPal == *palette)
|
||||
g->drawRgbaSurface(tick, bounds.x, bounds.y+bounds.h/2-tick->height()/2);
|
||||
}
|
||||
|
||||
bounds.x += tick->width();
|
||||
bounds.w -= tick->width();
|
||||
|
||||
gfx::Rect box(
|
||||
bounds.x, bounds.y+bounds.h-6*guiscale(),
|
||||
|
@ -28,7 +28,7 @@ namespace app {
|
||||
protected:
|
||||
virtual ResourceListItem* onCreateResourceItem(Resource* resource) override;
|
||||
virtual void onResourceChange(Resource* resource) override;
|
||||
virtual void onPaintResource(ui::Graphics* g, const gfx::Rect& bounds, Resource* resource) override;
|
||||
virtual void onPaintResource(ui::Graphics* g, gfx::Rect& bounds, Resource* resource) override;
|
||||
virtual void onResourceSizeHint(Resource* resource, gfx::Size& size) override;
|
||||
|
||||
ui::TooltipManager m_tooltips;
|
||||
|
@ -32,6 +32,7 @@ PopupWindowPin::PopupWindowPin(const std::string& text, ClickBehavior clickBehav
|
||||
{
|
||||
SkinTheme* theme = SkinTheme::instance();
|
||||
|
||||
m_pin.setFocusStop(false);
|
||||
m_pin.Click.connect(&PopupWindowPin::onPinClick, this);
|
||||
m_pin.setIconInterface(
|
||||
new ButtonIconImpl(theme->parts.unpinned(),
|
||||
|
@ -67,7 +67,7 @@ void ResourceListItem::onPaint(PaintEvent& ev)
|
||||
|
||||
g->drawString(text(), fgcolor, gfx::ColorNone,
|
||||
gfx::Point(
|
||||
bounds.x + guiscale()*2,
|
||||
bounds.x + 2*guiscale(),
|
||||
bounds.y + bounds.h/2 - g->measureUIString(text()).h/2));
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ Resource* ResourcesListBox::selectedResource()
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ResourcesListBox::paintResource(Graphics* g, const gfx::Rect& bounds, Resource* resource)
|
||||
void ResourcesListBox::paintResource(Graphics* g, gfx::Rect& bounds, Resource* resource)
|
||||
{
|
||||
onPaintResource(g, bounds, resource);
|
||||
}
|
||||
|
@ -47,11 +47,11 @@ class ResourceListItem : public ui::ListItem {
|
||||
|
||||
// abstract
|
||||
virtual void onResourceChange(Resource* resource) = 0;
|
||||
virtual void onPaintResource(ui::Graphics* g, const gfx::Rect& bounds, Resource* resource) = 0;
|
||||
virtual void onPaintResource(ui::Graphics* g, gfx::Rect& bounds, Resource* resource) = 0;
|
||||
virtual void onResourceSizeHint(Resource* resource, gfx::Size& size) = 0;
|
||||
|
||||
private:
|
||||
void paintResource(ui::Graphics* g, const gfx::Rect& bounds, Resource* resource);
|
||||
void paintResource(ui::Graphics* g, gfx::Rect& bounds, Resource* resource);
|
||||
gfx::Size resourceSizeHint(Resource* resource);
|
||||
|
||||
void onTick();
|
||||
|
@ -23,6 +23,7 @@ using namespace ui;
|
||||
class SelectAccelerator::KeyField : public ui::Entry {
|
||||
public:
|
||||
KeyField(const Accelerator& accel) : ui::Entry(256, "") {
|
||||
setTranslateDeadKeys(false);
|
||||
setExpansive(true);
|
||||
setFocusMagnet(true);
|
||||
setAccel(accel);
|
||||
@ -38,9 +39,13 @@ public:
|
||||
protected:
|
||||
bool onProcessMessage(Message* msg) override {
|
||||
switch (msg->type()) {
|
||||
|
||||
case kKeyDownMessage:
|
||||
if (hasFocus() && !isReadOnly()) {
|
||||
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
|
||||
if (!keymsg->scancode() && keymsg->unicodeChar() < 32)
|
||||
break;
|
||||
|
||||
KeyModifiers modifiers = keymsg->modifiers();
|
||||
|
||||
if (keymsg->scancode() == kKeySpace)
|
||||
|
@ -895,9 +895,13 @@ void SkinTheme::paintCheckBox(PaintEvent& ev)
|
||||
if (iconInterface)
|
||||
paintIcon(widget, g, iconInterface, icon.x, icon.y);
|
||||
|
||||
// draw focus
|
||||
if (look != WithoutBordersLook && widget->hasFocus())
|
||||
// Draw focus
|
||||
if (look != WithoutBordersLook &&
|
||||
(widget->hasFocus() || (iconInterface &&
|
||||
widget->text().empty() &&
|
||||
widget->hasMouseOver()))) {
|
||||
drawRect(g, bounds, parts.checkFocus().get(), gfx::ColorNone);
|
||||
}
|
||||
}
|
||||
|
||||
void SkinTheme::paintGrid(PaintEvent& ev)
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/range_utils.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/string.h"
|
||||
#include "doc/document_event.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
@ -475,7 +476,7 @@ public:
|
||||
case kMouseEnterMessage:
|
||||
if (Preferences::instance().statusBar.focusFrameFieldOnMouseover()) {
|
||||
requestFocus();
|
||||
selectText(0, -1);
|
||||
selectAllText();
|
||||
}
|
||||
break;
|
||||
|
||||
@ -488,13 +489,11 @@ public:
|
||||
scancode == kKeyEnterPad)) {
|
||||
Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoFrame);
|
||||
Params params;
|
||||
int frame = textInt();
|
||||
if (frame > 0) {
|
||||
params.set("frame", text().c_str());
|
||||
UIContext::instance()->executeCommand(cmd, params);
|
||||
}
|
||||
params.set("frame", text().c_str());
|
||||
UIContext::instance()->executeCommand(cmd, params);
|
||||
|
||||
// Select the text again
|
||||
selectText(0, -1);
|
||||
selectAllText();
|
||||
releaseFocus();
|
||||
return true; // Key used.
|
||||
}
|
||||
@ -611,17 +610,15 @@ void StatusBar::showBackupIcon(BackupIcon icon)
|
||||
m_indicators->showBackupIcon(icon);
|
||||
}
|
||||
|
||||
bool StatusBar::setStatusText(int msecs, const char *format, ...)
|
||||
bool StatusBar::setStatusText(int msecs, const char* format, ...)
|
||||
{
|
||||
if ((base::current_tick() > m_timeout) || (msecs > 0)) {
|
||||
char buf[256]; // TODO warning buffer overflow
|
||||
va_list ap;
|
||||
|
||||
std::va_list ap;
|
||||
va_start(ap, format);
|
||||
vsprintf(buf, format, ap);
|
||||
std::string msg = base::string_vprintf(format, ap);
|
||||
va_end(ap);
|
||||
|
||||
IndicatorsGeneration(m_indicators).add(buf);
|
||||
IndicatorsGeneration(m_indicators).add(msg.c_str());
|
||||
m_timeout = base::current_tick() + msecs;
|
||||
return true;
|
||||
}
|
||||
@ -629,21 +626,18 @@ bool StatusBar::setStatusText(int msecs, const char *format, ...)
|
||||
return false;
|
||||
}
|
||||
|
||||
void StatusBar::showTip(int msecs, const char *format, ...)
|
||||
void StatusBar::showTip(int msecs, const char* format, ...)
|
||||
{
|
||||
char buf[256]; // TODO warning buffer overflow
|
||||
va_list ap;
|
||||
int x, y;
|
||||
|
||||
std::va_list ap;
|
||||
va_start(ap, format);
|
||||
vsprintf(buf, format, ap);
|
||||
std::string msg = base::string_vprintf(format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (m_tipwindow == NULL) {
|
||||
m_tipwindow = new CustomizedTipWindow(buf);
|
||||
m_tipwindow = new CustomizedTipWindow(msg);
|
||||
}
|
||||
else {
|
||||
m_tipwindow->setText(buf);
|
||||
m_tipwindow->setText(msg);
|
||||
}
|
||||
|
||||
m_tipwindow->setInterval(msecs);
|
||||
@ -654,14 +648,14 @@ void StatusBar::showTip(int msecs, const char *format, ...)
|
||||
m_tipwindow->openWindow();
|
||||
m_tipwindow->remapWindow();
|
||||
|
||||
x = bounds().x2() - m_tipwindow->bounds().w;
|
||||
y = bounds().y - m_tipwindow->bounds().h;
|
||||
int x = bounds().x2() - m_tipwindow->bounds().w;
|
||||
int y = bounds().y - m_tipwindow->bounds().h;
|
||||
m_tipwindow->positionWindow(x, y);
|
||||
|
||||
m_tipwindow->startTimer();
|
||||
|
||||
// Set the text in status-bar (with inmediate timeout)
|
||||
IndicatorsGeneration(m_indicators).add(buf);
|
||||
IndicatorsGeneration(m_indicators).add(msg.c_str());
|
||||
m_timeout = base::current_tick();
|
||||
}
|
||||
|
||||
@ -744,13 +738,15 @@ void StatusBar::onActiveSiteChange(const doc::Site& site)
|
||||
ASSERT(m_doc == site.document());
|
||||
}
|
||||
|
||||
auto& docPref = Preferences::instance().document(
|
||||
static_cast<app::Document*>(m_doc));
|
||||
|
||||
m_docControls->setVisible(true);
|
||||
showSnapToGridWarning(
|
||||
Preferences::instance().document(
|
||||
static_cast<app::Document*>(m_doc)).grid.snap());
|
||||
showSnapToGridWarning(docPref.grid.snap());
|
||||
|
||||
// Current frame
|
||||
m_currentFrame->setTextf("%d", site.frame()+1);
|
||||
m_currentFrame->setTextf(
|
||||
"%d", site.frame()+docPref.timeline.firstFrame());
|
||||
}
|
||||
else {
|
||||
ASSERT(m_doc == nullptr);
|
||||
|
@ -57,8 +57,8 @@ namespace app {
|
||||
|
||||
void clearText();
|
||||
|
||||
bool setStatusText(int msecs, const char *format, ...);
|
||||
void showTip(int msecs, const char *format, ...);
|
||||
bool setStatusText(int msecs, const char* format, ...);
|
||||
void showTip(int msecs, const char* format, ...);
|
||||
void showColor(int msecs, const char* text, const Color& color);
|
||||
void showTool(int msecs, tools::Tool* tool);
|
||||
void showSnapToGridWarning(bool state);
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "gfx/rect.h"
|
||||
#include "she/font.h"
|
||||
#include "she/surface.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/scroll_helper.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
@ -250,6 +251,9 @@ void Timeline::updateUsingEditor(Editor* editor)
|
||||
m_hot.part = PART_NOTHING;
|
||||
m_clk.part = PART_NOTHING;
|
||||
|
||||
m_firstFrameConn = Preferences::instance().document(m_document)
|
||||
.timeline.firstFrame.AfterChange.connect(base::Bind<void>(&Timeline::invalidate, this));
|
||||
|
||||
setFocusStop(true);
|
||||
regenerateLayers();
|
||||
setViewScroll(viewScroll());
|
||||
@ -258,6 +262,8 @@ void Timeline::updateUsingEditor(Editor* editor)
|
||||
|
||||
void Timeline::detachDocument()
|
||||
{
|
||||
m_firstFrameConn.disconnect();
|
||||
|
||||
if (m_document) {
|
||||
m_thumbnailsPrefConn.disconnect();
|
||||
m_document->remove_observer(this);
|
||||
@ -425,7 +431,8 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
if (!m_document)
|
||||
break;
|
||||
|
||||
if (mouseMsg->middle() || she::is_key_pressed(kKeySpace)) {
|
||||
if (mouseMsg->middle() ||
|
||||
she::instance()->isKeyPressed(kKeySpace)) {
|
||||
captureMouse();
|
||||
m_state = STATE_SCROLLING;
|
||||
m_oldPos = static_cast<MouseMessage*>(msg)->position();
|
||||
@ -1048,7 +1055,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
m_scroll = false;
|
||||
|
||||
// We have to clear all the kKeySpace keys in buffer.
|
||||
she::clear_keyboard_buffer();
|
||||
she::instance()->clearKeyboardBuffer();
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
@ -1065,35 +1072,23 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
|
||||
case kMouseWheelMessage:
|
||||
if (m_document) {
|
||||
int base_size = skinTheme()->dimensions.timelineBaseSize();
|
||||
int dz = static_cast<MouseMessage*>(msg)->wheelDelta().y * base_size;
|
||||
|
||||
if (msg->altPressed()) {
|
||||
if (dz != 0) {
|
||||
double next_zoom = m_zoom + (dz < 0 ? 1 : -1);
|
||||
setZoomAndUpdate(next_zoom);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int dx;
|
||||
int dy;
|
||||
|
||||
if (msg->ctrlPressed()) {
|
||||
dx = dz;
|
||||
dy = 0;
|
||||
}
|
||||
else {
|
||||
dx = static_cast<MouseMessage*>(msg)->wheelDelta().x * base_size;
|
||||
dy = dz;
|
||||
}
|
||||
gfx::Point delta = static_cast<MouseMessage*>(msg)->wheelDelta();
|
||||
if (!static_cast<MouseMessage*>(msg)->preciseWheel()) {
|
||||
delta.x *= frameBoxWidth();
|
||||
delta.y *= layerBoxHeight();
|
||||
|
||||
if (msg->shiftPressed()) {
|
||||
dx *= frameBoxWidth() / base_size;
|
||||
dy *= layerBoxHeight() / base_size;
|
||||
// On macOS shift already changes the wheel axis
|
||||
if (std::fabs(delta.y) > delta.x)
|
||||
std::swap(delta.x, delta.y);
|
||||
}
|
||||
|
||||
setViewScroll(viewScroll() + gfx::Point(dx, dy));
|
||||
if (msg->altPressed()) {
|
||||
delta.x *= 3;
|
||||
delta.y *= 3;
|
||||
}
|
||||
}
|
||||
setViewScroll(viewScroll() + delta);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1589,8 +1584,11 @@ void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame)
|
||||
return;
|
||||
|
||||
// Draw the header for the layers.
|
||||
char buf[256];
|
||||
std::sprintf(buf, "%d", (frame+1)%100); // Draw only the first two digits.
|
||||
char buf[4];
|
||||
std::snprintf(
|
||||
buf, sizeof(buf), "%d",
|
||||
// Draw only the first two digits
|
||||
(docPref().timeline.firstFrame()+frame) % 100);
|
||||
|
||||
she::Font* oldFont = g->font();
|
||||
g->setFont(skinTheme()->getMiniFont());
|
||||
@ -2694,9 +2692,10 @@ void Timeline::updateStatusBar(ui::Message* msg)
|
||||
|
||||
case PART_HEADER_FRAME:
|
||||
if (validFrame(m_hot.frame)) {
|
||||
sb->setStatusText(0,
|
||||
sb->setStatusText(
|
||||
0,
|
||||
":frame: %d :clock: %d",
|
||||
(int)m_hot.frame+1,
|
||||
(int)m_hot.frame+docPref().timeline.firstFrame(),
|
||||
m_sprite->frameDuration(m_hot.frame));
|
||||
return;
|
||||
}
|
||||
|
@ -339,6 +339,7 @@ namespace app {
|
||||
// Configure timeline
|
||||
ConfigureTimelinePopup* m_confPopup;
|
||||
obs::scoped_connection m_ctxConn;
|
||||
obs::connection m_firstFrameConn;
|
||||
|
||||
// Marching ants stuff to show the range in the clipboard.
|
||||
// TODO merge this with the marching ants of the sprite editor (ui::Editor)
|
||||
|
@ -727,6 +727,11 @@ Rect ToolBar::ToolStrip::getToolBounds(int index)
|
||||
iconsize.w, bounds.h);
|
||||
}
|
||||
|
||||
void ToolBar::onActiveToolChange(tools::Tool* tool)
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ToolBar::onSelectedToolChange(tools::Tool* tool)
|
||||
{
|
||||
if (tool && m_selectedInGroup[tool->getGroup()] != tool)
|
||||
|
@ -61,6 +61,7 @@ namespace app {
|
||||
void onClosePopup();
|
||||
|
||||
// ActiveToolObserver impl
|
||||
void onActiveToolChange(tools::Tool* tool) override;
|
||||
void onSelectedToolChange(tools::Tool* tool) override;
|
||||
|
||||
// What tool is selected for each tool-group
|
||||
|
@ -296,6 +296,10 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
|
||||
else if (elem_name == "listbox") {
|
||||
if (!widget)
|
||||
widget = new ListBox();
|
||||
|
||||
bool multiselect = bool_attr_is_true(elem, "multiselect");
|
||||
if (multiselect)
|
||||
static_cast<ListBox*>(widget)->setMultiselect(multiselect);
|
||||
}
|
||||
else if (elem_name == "listitem") {
|
||||
ListItem* listitem;
|
||||
|
2
src/clip
@ -1 +1 @@
|
||||
Subproject commit 3e9533e332e6b1982476c255eedaae917e63dfec
|
||||
Subproject commit 926e3cf0ae484111d89cacd769d29c4d1e073dec
|
@ -2,7 +2,8 @@
|
||||
# Copyright (C) 2012-2016 David Capello
|
||||
|
||||
set(SHE_SOURCES
|
||||
common/freetype_font.cpp)
|
||||
common/freetype_font.cpp
|
||||
system.cpp)
|
||||
|
||||
######################################################################
|
||||
# Allegro 4 backend
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -21,14 +21,12 @@ int key_repeated[KEY_MAX];
|
||||
int she_keyboard_ucallback(int unicode_char, int* scancode)
|
||||
{
|
||||
int c = ((*scancode) & 0x7f);
|
||||
Event ev;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setScancode(static_cast<KeyScancode>(c));
|
||||
if (unicode_char > 0)
|
||||
ev.setUnicodeChar(unicode_char);
|
||||
else
|
||||
ev.setUnicodeChar(::scancode_to_ascii(c));
|
||||
ev.setRepeat(key_repeated[c]++);
|
||||
queue_event(ev);
|
||||
|
||||
|
@ -63,8 +63,6 @@
|
||||
#include "she/alleg4/mouse_poller.h"
|
||||
#endif
|
||||
|
||||
static she::System* g_instance = nullptr;
|
||||
|
||||
namespace she {
|
||||
|
||||
class Alleg4EventQueue : public EventQueue {
|
||||
@ -129,15 +127,11 @@ public:
|
||||
#ifdef USE_MOUSE_POLLER
|
||||
mouse_poller_init();
|
||||
#endif
|
||||
|
||||
g_instance = this;
|
||||
}
|
||||
|
||||
~Alleg4System() {
|
||||
remove_timer();
|
||||
allegro_exit();
|
||||
|
||||
g_instance = nullptr;
|
||||
}
|
||||
|
||||
void dispose() override {
|
||||
@ -206,21 +200,48 @@ public:
|
||||
return sur;
|
||||
}
|
||||
|
||||
bool isKeyPressed(KeyScancode scancode) override {
|
||||
#ifdef ALLEGRO_UNIX
|
||||
if (scancode == kKeyLShift || scancode == kKeyRShift) {
|
||||
return key_shifts & KB_SHIFT_FLAG;
|
||||
}
|
||||
else if (scancode == kKeyLControl || scancode == kKeyRControl) {
|
||||
return key_shifts & KB_CTRL_FLAG;
|
||||
}
|
||||
else if (scancode == kKeyAlt) {
|
||||
return key_shifts & KB_ALT_FLAG;
|
||||
}
|
||||
#endif
|
||||
return key[scancode] ? true: false;
|
||||
}
|
||||
|
||||
|
||||
int getUnicodeFromScancode(KeyScancode scancode) override {
|
||||
if (isKeyPressed(scancode))
|
||||
return scancode_to_ascii(scancode);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void clearKeyboardBuffer() override {
|
||||
clear_keybuf();
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) override {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
System* create_system() {
|
||||
System* create_system_impl() {
|
||||
return new Alleg4System();
|
||||
}
|
||||
|
||||
System* instance()
|
||||
{
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
void error_message(const char* msg)
|
||||
{
|
||||
if (g_instance && g_instance->logger())
|
||||
g_instance->logger()->logError(msg);
|
||||
System* sys = instance();
|
||||
if (sys && sys->logger())
|
||||
sys->logger()->logError(msg);
|
||||
|
||||
#ifdef _WIN32
|
||||
std::wstring wmsg = base::from_utf8(msg);
|
||||
@ -231,27 +252,6 @@ void error_message(const char* msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_key_pressed(KeyScancode scancode)
|
||||
{
|
||||
#ifdef ALLEGRO_UNIX
|
||||
if (scancode == kKeyLShift || scancode == kKeyRShift) {
|
||||
return key_shifts & KB_SHIFT_FLAG;
|
||||
}
|
||||
else if (scancode == kKeyLControl || scancode == kKeyRControl) {
|
||||
return key_shifts & KB_CTRL_FLAG;
|
||||
}
|
||||
else if (scancode == kKeyAlt) {
|
||||
return key_shifts & KB_ALT_FLAG;
|
||||
}
|
||||
#endif
|
||||
return key[scancode] ? true: false;
|
||||
}
|
||||
|
||||
void clear_keyboard_buffer()
|
||||
{
|
||||
clear_keybuf();
|
||||
}
|
||||
|
||||
} // namespace she
|
||||
|
||||
// It must be defined by the user program code.
|
||||
|
@ -78,6 +78,25 @@ public:
|
||||
return loadFreeTypeFont(filename, height);
|
||||
}
|
||||
|
||||
KeyModifiers keyModifiers() override {
|
||||
return
|
||||
(KeyModifiers)
|
||||
((isKeyPressed(kKeyLShift) ||
|
||||
isKeyPressed(kKeyRShift) ? kKeyShiftModifier: 0) |
|
||||
(isKeyPressed(kKeyLControl) ||
|
||||
isKeyPressed(kKeyRControl) ? kKeyCtrlModifier: 0) |
|
||||
(isKeyPressed(kKeyAlt) ? kKeyAltModifier: 0) |
|
||||
(isKeyPressed(kKeyAltGr) ? (kKeyCtrlModifier | kKeyAltModifier): 0) |
|
||||
(isKeyPressed(kKeyCommand) ? kKeyCmdModifier: 0) |
|
||||
(isKeyPressed(kKeySpace) ? kKeySpaceModifier: 0) |
|
||||
(isKeyPressed(kKeyLWin) ||
|
||||
isKeyPressed(kKeyRWin) ? kKeyWinModifier: 0));
|
||||
}
|
||||
|
||||
void clearKeyboardBuffer() override {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private:
|
||||
NativeDialogs* m_nativeDialogs;
|
||||
};
|
||||
|
@ -56,6 +56,7 @@ namespace she {
|
||||
m_scancode(kKeyNil),
|
||||
m_modifiers(kKeyUninitializedModifier),
|
||||
m_unicodeChar(0),
|
||||
m_isDead(false),
|
||||
m_repeat(0),
|
||||
m_preciseWheel(false),
|
||||
m_pointerType(PointerType::Unknown),
|
||||
@ -70,6 +71,7 @@ namespace she {
|
||||
KeyScancode scancode() const { return m_scancode; }
|
||||
KeyModifiers modifiers() const { return m_modifiers; }
|
||||
int unicodeChar() const { return m_unicodeChar; }
|
||||
bool isDeadKey() const { return m_isDead; }
|
||||
int repeat() const { return m_repeat; }
|
||||
gfx::Point position() const { return m_position; }
|
||||
gfx::Point wheelDelta() const { return m_wheelDelta; }
|
||||
@ -92,6 +94,7 @@ namespace she {
|
||||
void setScancode(KeyScancode scancode) { m_scancode = scancode; }
|
||||
void setModifiers(KeyModifiers modifiers) { m_modifiers = modifiers; }
|
||||
void setUnicodeChar(int unicodeChar) { m_unicodeChar = unicodeChar; }
|
||||
void setDeadKey(bool state) { m_isDead = state; }
|
||||
void setRepeat(int repeat) { m_repeat = repeat; }
|
||||
void setPosition(const gfx::Point& pos) { m_position = pos; }
|
||||
void setWheelDelta(const gfx::Point& delta) { m_wheelDelta = delta; }
|
||||
@ -108,6 +111,7 @@ namespace she {
|
||||
KeyScancode m_scancode;
|
||||
KeyModifiers m_modifiers;
|
||||
int m_unicodeChar;
|
||||
bool m_isDead;
|
||||
int m_repeat; // repeat=0 means the first time the key is pressed
|
||||
gfx::Point m_position;
|
||||
gfx::Point m_wheelDelta;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2013, 2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -125,30 +125,30 @@ namespace she {
|
||||
kKeyCircumflex = 100,
|
||||
kKeyColon2 = 101,
|
||||
kKeyKanji = 102,
|
||||
kKeyEqualsPad = 103, // MacOS X
|
||||
kKeyBackquote = 104, // MacOS X
|
||||
kKeySemicolon = 105, // MacOS X
|
||||
kKeyCommand = 106, // MacOS X
|
||||
kKeyUnknown1 = 107,
|
||||
kKeyUnknown2 = 108,
|
||||
kKeyUnknown3 = 109,
|
||||
kKeyUnknown4 = 110,
|
||||
kKeyUnknown5 = 111,
|
||||
kKeyUnknown6 = 112,
|
||||
kKeyUnknown7 = 113,
|
||||
kKeyUnknown8 = 114,
|
||||
kKeyEqualsPad = 103, // macOS
|
||||
kKeyBackquote = 104, // macOS
|
||||
kKeySemicolon = 105, // macOS
|
||||
kKeyUnknown1 = 106,
|
||||
kKeyUnknown2 = 107,
|
||||
kKeyUnknown3 = 108,
|
||||
kKeyUnknown4 = 109,
|
||||
kKeyUnknown5 = 110,
|
||||
kKeyUnknown6 = 111,
|
||||
kKeyUnknown7 = 112,
|
||||
kKeyUnknown8 = 113,
|
||||
|
||||
kKeyFirstModifierScancode = 115,
|
||||
kKeyFirstModifierScancode = 114,
|
||||
|
||||
kKeyLShift = 115,
|
||||
kKeyRShift = 116,
|
||||
kKeyLControl = 117,
|
||||
kKeyRControl = 118,
|
||||
kKeyAlt = 119,
|
||||
kKeyAltGr = 120,
|
||||
kKeyLWin = 121,
|
||||
kKeyRWin = 122,
|
||||
kKeyMenu = 123,
|
||||
kKeyLShift = 114,
|
||||
kKeyRShift = 115,
|
||||
kKeyLControl = 116,
|
||||
kKeyRControl = 117,
|
||||
kKeyAlt = 118,
|
||||
kKeyAltGr = 119,
|
||||
kKeyLWin = 120,
|
||||
kKeyRWin = 121,
|
||||
kKeyMenu = 122,
|
||||
kKeyCommand = 123, // macOS - TODO This should be inside the modifiers range
|
||||
kKeyScrLock = 124,
|
||||
kKeyNumLock = 125,
|
||||
kKeyCapsLock = 126,
|
||||
@ -156,11 +156,6 @@ namespace she {
|
||||
kKeyScancodes = 127
|
||||
};
|
||||
|
||||
// Deprecated API, use modifiers in she::Event
|
||||
// TODO mark these functions as deprecated
|
||||
bool is_key_pressed(KeyScancode scancode);
|
||||
void clear_keyboard_buffer();
|
||||
|
||||
} // namespace she
|
||||
|
||||
#endif
|
||||
|
@ -32,12 +32,11 @@ retry:;
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (event) {
|
||||
// Intercept Control+Tab and send it to the main NSView. Without
|
||||
// this, the NSApplication intercepts the key combination and
|
||||
// use it to go to the next key view.
|
||||
if (event.type == NSKeyDown &&
|
||||
event.modifierFlags & NSControlKeyMask &&
|
||||
event.keyCode == kVK_Tab) {
|
||||
// Intercept <Control+Tab>, <Cmd+[>, and other keyboard
|
||||
// combinations, and send them directly to the main
|
||||
// NSView. Without this, the NSApplication intercepts the key
|
||||
// combination and use it to go to the next key view.
|
||||
if (event.type == NSKeyDown) {
|
||||
[app.mainWindow.contentView keyDown:event];
|
||||
}
|
||||
else {
|
||||
|
31
src/she/osx/system.h
Normal file
@ -0,0 +1,31 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef SHE_OSX_SYSTEM_H
|
||||
#define SHE_OSX_SYSTEM_H
|
||||
#pragma once
|
||||
|
||||
#include "she/common/system.h"
|
||||
|
||||
namespace she {
|
||||
|
||||
bool osx_is_key_pressed(KeyScancode scancode);
|
||||
int osx_get_unicode_from_scancode(KeyScancode scancode);
|
||||
|
||||
class OSXSystem : public CommonSystem {
|
||||
public:
|
||||
bool isKeyPressed(KeyScancode scancode) override {
|
||||
return osx_is_key_pressed(scancode);
|
||||
}
|
||||
|
||||
int getUnicodeFromScancode(KeyScancode scancode) override {
|
||||
return osx_get_unicode_from_scancode(scancode);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace she
|
||||
|
||||
#endif
|
@ -53,6 +53,8 @@
|
||||
- (void)updateCurrentCursor;
|
||||
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender;
|
||||
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender;
|
||||
- (void)doCommandBySelector:(SEL)selector;
|
||||
- (void)setTranslateDeadKeys:(BOOL)state;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
@ -16,15 +16,22 @@
|
||||
#include "she/keys.h"
|
||||
#include "she/osx/generate_drop_files.h"
|
||||
#include "she/osx/window.h"
|
||||
#include "she/system.h"
|
||||
|
||||
using namespace she;
|
||||
#include <Carbon/Carbon.h> // For VK codes
|
||||
|
||||
namespace she {
|
||||
|
||||
bool osx_is_key_pressed(KeyScancode scancode);
|
||||
|
||||
namespace {
|
||||
|
||||
// Internal array of pressed keys used in is_key_pressed()
|
||||
bool pressed_keys[kKeyScancodes];
|
||||
// Internal array of pressed keys used in isKeyPressed()
|
||||
int g_pressedKeys[kKeyScancodes];
|
||||
bool g_translateDeadKeys = false;
|
||||
UInt32 g_lastDeadKeyState = 0;
|
||||
|
||||
inline gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
{
|
||||
NSPoint point = [view convertPoint:[event locationInWindow]
|
||||
fromView:nil];
|
||||
@ -37,7 +44,7 @@ inline gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
(view.bounds.size.height - point.y) / scale);
|
||||
}
|
||||
|
||||
inline Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
{
|
||||
// Some Wacom drivers on OS X report right-clicks with
|
||||
// buttonNumber=0, so we've to check the type event anyway.
|
||||
@ -61,7 +68,7 @@ inline Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
return Event::MouseButton::NoneButton;
|
||||
}
|
||||
|
||||
inline KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
{
|
||||
int modifiers = kKeyNoneModifier;
|
||||
NSEventModifierFlags nsFlags = event.modifierFlags;
|
||||
@ -69,37 +76,71 @@ inline KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
if (nsFlags & NSControlKeyMask) modifiers |= kKeyCtrlModifier;
|
||||
if (nsFlags & NSAlternateKeyMask) modifiers |= kKeyAltModifier;
|
||||
if (nsFlags & NSCommandKeyMask) modifiers |= kKeyCmdModifier;
|
||||
if (she::is_key_pressed(kKeySpace)) modifiers |= kKeySpaceModifier;
|
||||
if (osx_is_key_pressed(kKeySpace)) modifiers |= kKeySpaceModifier;
|
||||
return (KeyModifiers)modifiers;
|
||||
}
|
||||
|
||||
inline int get_unicodechar_from_nsevent(NSEvent* event)
|
||||
// Based on code from:
|
||||
// http://stackoverflow.com/questions/22566665/how-to-capture-unicode-from-key-events-without-an-nstextview
|
||||
// http://stackoverflow.com/questions/12547007/convert-key-code-into-key-equivalent-string
|
||||
// http://stackoverflow.com/questions/8263618/convert-virtual-key-code-to-unicode-string
|
||||
//
|
||||
// It includes a "translateDeadKeys" flag to avoid processing dead
|
||||
// keys in case that we want to use key
|
||||
CFStringRef get_unicode_from_key_code(NSEvent* event,
|
||||
const bool translateDeadKeys)
|
||||
{
|
||||
int chr = 0;
|
||||
// TODO we should use "event.characters" for a new kind of event (Event::Char or something like that)
|
||||
NSString* chars = event.charactersIgnoringModifiers;
|
||||
if (chars && chars.length >= 1) {
|
||||
chr = [chars characterAtIndex:0];
|
||||
if (chr < 32)
|
||||
chr = 0;
|
||||
}
|
||||
return chr;
|
||||
TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource();
|
||||
CFDataRef keyLayoutData = (CFDataRef)TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData);
|
||||
const UCKeyboardLayout* keyLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(keyLayoutData);
|
||||
|
||||
UInt32 deadKeyState = (translateDeadKeys ? g_lastDeadKeyState: 0);
|
||||
UniChar output[4];
|
||||
UniCharCount length;
|
||||
|
||||
// Reference here:
|
||||
// https://developer.apple.com/reference/coreservices/1390584-uckeytranslate?language=objc
|
||||
UCKeyTranslate(
|
||||
keyLayout,
|
||||
event.keyCode,
|
||||
kUCKeyActionDown,
|
||||
((event.modifierFlags >> 16) & 0xFF),
|
||||
LMGetKbdType(),
|
||||
(translateDeadKeys ? 0: kUCKeyTranslateNoDeadKeysMask),
|
||||
&deadKeyState,
|
||||
sizeof(output) / sizeof(output[0]),
|
||||
&length,
|
||||
output);
|
||||
|
||||
if (translateDeadKeys)
|
||||
g_lastDeadKeyState = deadKeyState;
|
||||
|
||||
CFRelease(inputSource);
|
||||
return CFStringCreateWithCharacters(kCFAllocatorDefault, output, length);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace she {
|
||||
|
||||
bool is_key_pressed(KeyScancode scancode)
|
||||
bool osx_is_key_pressed(KeyScancode scancode)
|
||||
{
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
return pressed_keys[scancode];
|
||||
return (g_pressedKeys[scancode] != 0);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int osx_get_unicode_from_scancode(KeyScancode scancode)
|
||||
{
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
return g_pressedKeys[scancode];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace she
|
||||
|
||||
using namespace she;
|
||||
|
||||
@implementation OSXView
|
||||
|
||||
- (id)initWithFrame:(NSRect)frameRect
|
||||
@ -167,17 +208,47 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
[super keyDown:event];
|
||||
|
||||
KeyScancode scancode = cocoavk_to_scancode(event.keyCode);
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
pressed_keys[scancode] = true;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setScancode(scancode);
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setRepeat(event.ARepeat ? 1: 0);
|
||||
ev.setUnicodeChar(get_unicodechar_from_nsevent(event));
|
||||
ev.setUnicodeChar(0);
|
||||
|
||||
queue_event(ev);
|
||||
bool sendMsg = true;
|
||||
|
||||
CFStringRef strRef = get_unicode_from_key_code(event, false);
|
||||
if (strRef) {
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length == 1)
|
||||
ev.setUnicodeChar(CFStringGetCharacterAtIndex(strRef, 0));
|
||||
CFRelease(strRef);
|
||||
}
|
||||
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
g_pressedKeys[scancode] = (ev.unicodeChar() ? ev.unicodeChar(): 1);
|
||||
|
||||
if (g_translateDeadKeys) {
|
||||
strRef = get_unicode_from_key_code(event, true);
|
||||
if (strRef) {
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length > 0) {
|
||||
sendMsg = false;
|
||||
for (int i=0; i<length; ++i) {
|
||||
ev.setUnicodeChar(CFStringGetCharacterAtIndex(strRef, i));
|
||||
queue_event(ev);
|
||||
}
|
||||
g_lastDeadKeyState = 0;
|
||||
}
|
||||
else {
|
||||
ev.setDeadKey(true);
|
||||
}
|
||||
CFRelease(strRef);
|
||||
}
|
||||
}
|
||||
|
||||
if (sendMsg)
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent*)event
|
||||
@ -186,14 +257,14 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
|
||||
KeyScancode scancode = cocoavk_to_scancode(event.keyCode);
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
pressed_keys[scancode] = false;
|
||||
g_pressedKeys[scancode] = 0;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyUp);
|
||||
ev.setScancode(scancode);
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setRepeat(event.ARepeat ? 1: 0);
|
||||
ev.setUnicodeChar(get_unicodechar_from_nsevent(event));
|
||||
ev.setUnicodeChar(0);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
@ -230,7 +301,7 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
((newFlags & flags[i]) != 0 ? Event::KeyDown:
|
||||
Event::KeyUp));
|
||||
|
||||
pressed_keys[scancodes[i]] = ((newFlags & flags[i]) != 0);
|
||||
g_pressedKeys[scancodes[i]] = ((newFlags & flags[i]) != 0);
|
||||
|
||||
ev.setScancode(scancodes[i]);
|
||||
ev.setModifiers(modifiers);
|
||||
@ -517,4 +588,15 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)selector
|
||||
{
|
||||
// Do nothing (avoid beep pressing Escape key)
|
||||
}
|
||||
|
||||
- (void)setTranslateDeadKeys:(BOOL)state
|
||||
{
|
||||
g_translateDeadKeys = (state ? true: false);
|
||||
g_lastDeadKeyState = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -24,15 +24,8 @@
|
||||
|
||||
namespace she {
|
||||
|
||||
static System* g_instance;
|
||||
|
||||
System* create_system() {
|
||||
return g_instance = new SkiaSystem();
|
||||
}
|
||||
|
||||
System* instance()
|
||||
{
|
||||
return g_instance;
|
||||
System* create_system_impl() {
|
||||
return new SkiaSystem();
|
||||
}
|
||||
|
||||
void error_message(const char* msg)
|
||||
@ -41,19 +34,11 @@ void error_message(const char* msg)
|
||||
// TODO
|
||||
}
|
||||
|
||||
void clear_keyboard_buffer()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
} // namespace she
|
||||
|
||||
extern int app_main(int argc, char* argv[]);
|
||||
|
||||
#if _WIN32
|
||||
extern int __argc;
|
||||
extern wchar_t** __wargv;
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
PWSTR lpCmdLine, int nCmdShow) {
|
||||
int argc = __argc;
|
||||
|
@ -184,6 +184,11 @@ void SkiaDisplay::setLayout(const std::string& layout)
|
||||
m_window.setLayout(layout);
|
||||
}
|
||||
|
||||
void SkiaDisplay::setTranslateDeadKeys(bool state)
|
||||
{
|
||||
m_window.setTranslateDeadKeys(state);
|
||||
}
|
||||
|
||||
DisplayHandle SkiaDisplay::nativeHandle()
|
||||
{
|
||||
return (DisplayHandle)m_window.handle();
|
||||
|
@ -60,6 +60,8 @@ public:
|
||||
std::string getLayout() override;
|
||||
void setLayout(const std::string& layout) override;
|
||||
|
||||
void setTranslateDeadKeys(bool state);
|
||||
|
||||
// Returns the HWND on Windows.
|
||||
DisplayHandle nativeHandle() override;
|
||||
|
||||
|
@ -21,7 +21,8 @@
|
||||
#elif __APPLE__
|
||||
#include "she/osx/app.h"
|
||||
#include "she/osx/event_queue.h"
|
||||
#define SkiaSystemBase CommonSystem
|
||||
#include "she/osx/system.h"
|
||||
#define SkiaSystemBase OSXSystem
|
||||
#else
|
||||
#include "she/x11/event_queue.h"
|
||||
#define SkiaSystemBase CommonSystem
|
||||
@ -135,6 +136,15 @@ public:
|
||||
return loadSurface(filename);
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) override {
|
||||
if (m_defaultDisplay)
|
||||
m_defaultDisplay->setTranslateDeadKeys(state);
|
||||
|
||||
#ifdef _WIN32
|
||||
g_queue.setTranslateDeadKeys(state);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
SkiaDisplay* m_defaultDisplay;
|
||||
bool m_gpuAcceleration;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -46,6 +46,7 @@ public:
|
||||
void updateWindow(const gfx::Rect& bounds);
|
||||
std::string getLayout() { return ""; }
|
||||
void setLayout(const std::string& layout) { }
|
||||
void setTranslateDeadKeys(bool state);
|
||||
void* handle();
|
||||
|
||||
private:
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "gfx/size.h"
|
||||
#include "she/event.h"
|
||||
#include "she/event_queue.h"
|
||||
#include "she/osx/view.h"
|
||||
#include "she/osx/window.h"
|
||||
#include "she/skia/skia_display.h"
|
||||
#include "she/skia/skia_surface.h"
|
||||
@ -122,6 +123,11 @@ public:
|
||||
[view displayIfNeeded];
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) {
|
||||
OSXView* view = (OSXView*)m_window.contentView;
|
||||
[view setTranslateDeadKeys:(state ? YES: NO)];
|
||||
}
|
||||
|
||||
void* handle() {
|
||||
return (__bridge void*)m_window;
|
||||
}
|
||||
@ -450,6 +456,12 @@ void SkiaWindow::updateWindow(const gfx::Rect& bounds)
|
||||
m_impl->updateWindow(bounds);
|
||||
}
|
||||
|
||||
void SkiaWindow::setTranslateDeadKeys(bool state)
|
||||
{
|
||||
if (m_impl)
|
||||
m_impl->setTranslateDeadKeys(state);
|
||||
}
|
||||
|
||||
void* SkiaWindow::handle()
|
||||
{
|
||||
if (m_impl)
|
||||
|
@ -44,6 +44,10 @@ public:
|
||||
std::string getLayout() { return ""; }
|
||||
void setLayout(const std::string& layout) { }
|
||||
|
||||
void setTranslateDeadKeys(bool state) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private:
|
||||
void onExpose() override;
|
||||
|
||||
|
31
src/she/system.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "she/system.h"
|
||||
|
||||
namespace she {
|
||||
|
||||
static System* g_system = nullptr;
|
||||
|
||||
System* create_system_impl(); // Defined on each back-end
|
||||
|
||||
System* create_system()
|
||||
{
|
||||
ASSERT(!g_system);
|
||||
return g_system = create_system_impl();
|
||||
}
|
||||
|
||||
System* instance()
|
||||
{
|
||||
return g_system;
|
||||
}
|
||||
|
||||
} // namespace she
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "gfx/fwd.h"
|
||||
#include "she/capabilities.h"
|
||||
#include "she/keys.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@ -49,6 +50,26 @@ namespace she {
|
||||
virtual Surface* loadRgbaSurface(const char* filename) = 0;
|
||||
virtual Font* loadSpriteSheetFont(const char* filename, int scale = 1) = 0;
|
||||
virtual Font* loadTrueTypeFont(const char* filename, int height) = 0;
|
||||
|
||||
// Returns true if the the given scancode key is pressed/actived.
|
||||
virtual bool isKeyPressed(KeyScancode scancode) = 0;
|
||||
|
||||
// Returns the active pressed modifiers.
|
||||
virtual KeyModifiers keyModifiers() = 0;
|
||||
|
||||
// Returns the latest unicode character that activated the given
|
||||
// scancode.
|
||||
virtual int getUnicodeFromScancode(KeyScancode scancode) = 0;
|
||||
|
||||
// Clears the keyboard buffer (used only in the Allegro port).
|
||||
// TODO (deprecated)
|
||||
virtual void clearKeyboardBuffer() = 0;
|
||||
|
||||
// Indicates if you want to use dead keys or not. By default it's
|
||||
// false, which behaves as regular shortcuts. You should set this
|
||||
// to true when you're inside a text field in your app.
|
||||
virtual void setTranslateDeadKeys(bool state) = 0;
|
||||
|
||||
};
|
||||
|
||||
System* create_system();
|
||||
|