From 20ff386421995043bd58bac8a9ce5918f3b48f11 Mon Sep 17 00:00:00 2001 From: Elinsrc <99191833+Elinsrc@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:57:09 +0500 Subject: [PATCH 1/2] added qt6 support --- .github/workflows/build.yml | 68 +- .gitignore | 3 + .gitmodules | 9 - CMakeLists.txt | 116 ++- external/glfw | 1 - external/imgui | 1 - external/portable-file-dialogs | 1 - external/stb | 2 +- resources/add_photo.png | Bin 0 -> 898 bytes resources/center_focus.png | Bin 0 -> 984 bytes resources/close.png | Bin 0 -> 711 bytes resources/folder.png | Bin 0 -> 602 bytes resources/info.png | Bin 0 -> 1168 bytes resources/navigate_before.png | Bin 0 -> 499 bytes resources/navigate_next.png | Bin 0 -> 489 bytes resources/pause.png | Bin 0 -> 379 bytes resources/play.png | Bin 0 -> 651 bytes resources/resources.qrc | 18 + resources/save_alt.png | Bin 0 -> 684 bytes resources/skip_next.png | Bin 0 -> 651 bytes resources/skip_previous.png | Bin 0 -> 687 bytes resources/zoom_in.png | Bin 0 -> 1405 bytes resources/zoom_out.png | Bin 0 -> 1361 bytes src/about_dialog.cpp | 41 + src/about_dialog.h | 10 + src/app_state.h | 40 + src/export_dialog.cpp | 151 +++ src/export_dialog.h | 26 + src/frame_slider.cpp | 145 +++ src/frame_slider.h | 35 + src/icons/center_focus_strong.h | 85 -- src/icons/folder.h | 50 - src/icons/icons.h | 10 - src/icons/navigate_before.h | 45 - src/icons/navigate_next.h | 44 - src/icons/pause.h | 35 - src/icons/play.h | 58 -- src/icons/skip_next.h | 58 -- src/icons/skip_previous.h | 61 -- src/icons/zoom_in.h | 121 --- src/icons/zoom_out.h | 117 --- src/import_dialog.cpp | 302 ++++++ src/import_dialog.h | 59 ++ src/main.cpp | 104 +- src/mainwindow.cpp | 708 +++++++++++++ src/mainwindow.h | 115 +++ src/properties_panel.cpp | 219 ++++ src/properties_panel.h | 36 + src/renderer.cpp | 52 - src/renderer.h | 21 - src/sprite_viewport.cpp | 214 ++++ src/sprite_viewport.h | 48 + src/theme.h | 393 +++++++ src/ui.cpp | 1700 ------------------------------- src/ui.h | 145 --- src/ui_utils.cpp | 29 - src/ui_utils.h | 19 - 57 files changed, 2702 insertions(+), 2813 deletions(-) delete mode 160000 external/glfw delete mode 160000 external/imgui delete mode 160000 external/portable-file-dialogs create mode 100644 resources/add_photo.png create mode 100644 resources/center_focus.png create mode 100644 resources/close.png create mode 100644 resources/folder.png create mode 100644 resources/info.png create mode 100644 resources/navigate_before.png create mode 100644 resources/navigate_next.png create mode 100644 resources/pause.png create mode 100644 resources/play.png create mode 100644 resources/resources.qrc create mode 100644 resources/save_alt.png create mode 100644 resources/skip_next.png create mode 100644 resources/skip_previous.png create mode 100644 resources/zoom_in.png create mode 100644 resources/zoom_out.png create mode 100644 src/about_dialog.cpp create mode 100644 src/about_dialog.h create mode 100644 src/app_state.h create mode 100644 src/export_dialog.cpp create mode 100644 src/export_dialog.h create mode 100644 src/frame_slider.cpp create mode 100644 src/frame_slider.h delete mode 100644 src/icons/center_focus_strong.h delete mode 100644 src/icons/folder.h delete mode 100644 src/icons/icons.h delete mode 100644 src/icons/navigate_before.h delete mode 100644 src/icons/navigate_next.h delete mode 100644 src/icons/pause.h delete mode 100644 src/icons/play.h delete mode 100644 src/icons/skip_next.h delete mode 100644 src/icons/skip_previous.h delete mode 100644 src/icons/zoom_in.h delete mode 100644 src/icons/zoom_out.h create mode 100644 src/import_dialog.cpp create mode 100644 src/import_dialog.h create mode 100644 src/mainwindow.cpp create mode 100644 src/mainwindow.h create mode 100644 src/properties_panel.cpp create mode 100644 src/properties_panel.h delete mode 100644 src/renderer.cpp delete mode 100644 src/renderer.h create mode 100644 src/sprite_viewport.cpp create mode 100644 src/sprite_viewport.h create mode 100644 src/theme.h delete mode 100644 src/ui.cpp delete mode 100644 src/ui.h delete mode 100644 src/ui_utils.cpp delete mode 100644 src/ui_utils.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7cc0c04..f4af679 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,37 +51,36 @@ jobs: with: submodules: recursive - - name: Install development tools + - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y \ - build-essential \ - cmake \ - libgl1-mesa-dev \ - libglfw3-dev \ - libx11-dev \ - libxrandr-dev \ - libxinerama-dev \ - libxcursor-dev \ - libxi-dev \ - libwayland-dev \ - libxkbcommon-dev - - - name: Build on Linux + sudo apt-get install -y build-essential cmake python3-pip libgl1-mesa-dev + pip install aqtinstall + + - name: Install Qt 6.10.0 + run: python3 -m aqt install-qt linux desktop 6.10.0 linux_gcc_64 --outputdir external + + - name: Build and Install run: | cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --config Release + cmake --install build --prefix "${{ github.workspace }}/dist" --config Release - name: Extract branch name shell: bash id: extract_branch run: echo "branch=$(echo ${GITHUB_REF#refs/heads/} | tr '/' '_')" >> $GITHUB_OUTPUT + - name: Prepare artifact structure + run: | + mkdir -p release/Sprite-Tools + cp -r dist/* release/Sprite-Tools/ + - name: Upload artifact uses: actions/upload-artifact@v4 with: name: Sprite-Tools-${{ steps.extract_branch.outputs.branch }}-linux - path: build/SpriteTools + path: release windows: name: Windows @@ -91,27 +90,34 @@ jobs: uses: actions/checkout@v5 with: submodules: recursive + + - name: Install aqtinstall + run: pip install aqtinstall - - name: Set developer command prompt - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 + - name: Install Qt 6.10.0 + run: python -m aqt install-qt windows desktop 6.10.0 win64_msvc2022_64 --outputdir external - - name: Build on Windows + - name: Build and Install run: | cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --config Release + cmake --install build --prefix "${{ github.workspace }}/dist" --config Release - name: Extract branch name shell: bash id: extract_branch run: echo "branch=$(echo ${GITHUB_REF#refs/heads/} | tr '/' '_')" >> $GITHUB_OUTPUT + - name: Prepare artifact structure + run: | + mkdir -p release/Sprite-Tools + cp -r dist/* release/Sprite-Tools/ + - name: Upload artifact uses: actions/upload-artifact@v4 with: name: Sprite-Tools-${{ steps.extract_branch.outputs.branch }}-windows - path: build/Release/SpriteTools.exe + path: release release: name: Upload release @@ -129,7 +135,6 @@ jobs: uses: actions/download-artifact@v4 with: path: artifacts - merge-multiple: true - name: Set tag name run: | @@ -141,6 +146,16 @@ jobs: RELEASE_NAME="Sprite-Tools $TAG_NAME Build" echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV + + - name: Pack Archives + run: | + mkdir final_assets + cd artifacts + for dir in */; do + zip_name="${dir%/}.zip" + echo "Creating $zip_name..." + cd "$dir" && zip -r "../../final_assets/$zip_name" . && cd .. + done - name: Remove old release if exists run: | @@ -150,17 +165,12 @@ jobs: fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: List downloaded artifacts - run: | - echo "Listing all artifacts in artifacts/:" - ls -R artifacts/ - name: Upload new release run: | gh release create "$TAG_NAME" \ --title "$RELEASE_NAME" \ - --prerelease artifacts/* \ + --prerelease final_assets/* \ --repo ${{ github.repository }} \ --target ${{ github.sha }} env: diff --git a/.gitignore b/.gitignore index 8cdb5c7..e7a749e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ CMakeSettings.json CMakeFiles CMakeCache.txt Makefile +external/6.10.0 +dist/ +aqtinstall.log \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index dd84f8f..ee3bc5e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,3 @@ [submodule "external/stb"] path = external/stb url = https://github.com/nothings/stb -[submodule "external/portable-file-dialogs"] - path = external/portable-file-dialogs - url = https://github.com/samhocevar/portable-file-dialogs -[submodule "external/imgui"] - path = external/imgui - url = https://github.com/ocornut/imgui -[submodule "external/glfw"] - path = external/glfw - url = https://github.com/glfw/glfw.git diff --git a/CMakeLists.txt b/CMakeLists.txt index fa9e209..270f30e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,16 @@ cmake_minimum_required(VERSION 3.18) + +if(POLICY CMP0177) + cmake_policy(SET CMP0177 NEW) +endif() + project(SpriteTools) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + if(ANDROID) set(PLATFORM_ANDROID TRUE) message(STATUS "Platform: Android") @@ -16,7 +23,6 @@ elseif(UNIX) endif() if(PLATFORM_ANDROID) - add_library(${PROJECT_NAME} SHARED src/core/sprite_converter_capi.cpp src/core/sprite_converter.cpp @@ -25,54 +31,104 @@ if(PLATFORM_ANDROID) src/core/sprite_jni.cpp src/core/stb_impl.cpp ) - - target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/core external/stb) - + target_include_directories(${PROJECT_NAME} PRIVATE src/core external/stb) target_link_libraries(${PROJECT_NAME} android log) else() + set(CMAKE_AUTOMOC ON) + set(CMAKE_AUTORCC ON) + set(CMAKE_AUTOUIC ON) + + if(PLATFORM_WIN32) + set(QT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/6.10.0/msvc2022_64") + else() + set(QT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/6.10.0/gcc_64") + endif() - add_subdirectory(external/glfw) + set(CMAKE_PREFIX_PATH ${QT_ROOT}) + + find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets NO_DEFAULT_PATH PATHS ${QT_ROOT}) + + qt_standard_project_setup() set(SOURCES src/main.cpp - src/ui.cpp - src/ui_utils.cpp + src/mainwindow.cpp + src/sprite_viewport.cpp + src/properties_panel.cpp + src/export_dialog.cpp + src/import_dialog.cpp + src/about_dialog.cpp + src/frame_slider.cpp src/core/stb_impl.cpp src/core/sprite_loader.cpp src/core/sprite_converter.cpp - src/renderer.cpp - - external/imgui/imgui.cpp - external/imgui/imgui_demo.cpp - external/imgui/imgui_draw.cpp - external/imgui/imgui_tables.cpp - external/imgui/imgui_widgets.cpp - external/imgui/backends/imgui_impl_glfw.cpp - external/imgui/backends/imgui_impl_opengl3.cpp + resources/resources.qrc ) - add_executable(${PROJECT_NAME} ${SOURCES}) - - target_include_directories(${PROJECT_NAME} PRIVATE - src - src/core - src/icons - external/imgui - external/imgui/backends - external/stb - external/portable-file-dialogs + set(HEADERS + src/mainwindow.h + src/sprite_viewport.h + src/properties_panel.h + src/export_dialog.h + src/import_dialog.h + src/about_dialog.h + src/frame_slider.h + src/app_state.h + src/theme.h ) + add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS}) + + target_include_directories(${PROJECT_NAME} PRIVATE src src/core external/stb) + target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets) + if(PLATFORM_WIN32) target_compile_definitions(${PROJECT_NAME} PRIVATE PLATFORM_WIN32) - target_link_libraries(${PROJECT_NAME} PRIVATE glfw opengl32) + set(LIB_DEST ".") endif() if(PLATFORM_LINUX) - find_package(OpenGL REQUIRED) target_compile_definitions(${PROJECT_NAME} PRIVATE PLATFORM_LINUX) - target_link_libraries(${PROJECT_NAME} PRIVATE glfw OpenGL::GL) + set(LIB_DEST "lib") + + set_target_properties(${PROJECT_NAME} PROPERTIES + BUILD_RPATH "${QT_ROOT}/lib" + INSTALL_RPATH "$ORIGIN/lib" + BUILD_WITH_INSTALL_RPATH FALSE + ) endif() -endif() \ No newline at end of file + install(TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION . + LIBRARY DESTINATION ${LIB_DEST} + ) + + set(QT_DEPLOY_BIN_DIR ".") + set(QT_DEPLOY_LIB_DIR "${LIB_DEST}") + set(QT_DEPLOY_PLUGINS_DIR "plugins") + + if(IS_ABSOLUTE "${CMAKE_INSTALL_PREFIX}") + set(QT_DEPLOY_QT_CONF_PATH "${CMAKE_INSTALL_PREFIX}/qt.conf") + else() + get_filename_component(ABS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) + set(QT_DEPLOY_QT_CONF_PATH "${ABS_INSTALL_PREFIX}/qt.conf") + endif() + + qt_generate_deploy_app_script( + TARGET ${PROJECT_NAME} + OUTPUT_SCRIPT deploy_script + NO_TRANSLATIONS + POST_EXCLUDE_REGEXES "" + ) + + install(SCRIPT ${deploy_script}) + + if(PLATFORM_LINUX) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + "[Paths]\nPrefix = .\nLibraries = lib\nPlugins = plugins\n" + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" DESTINATION .) + endif() + +endif() diff --git a/external/glfw b/external/glfw deleted file mode 160000 index fdd14e6..0000000 --- a/external/glfw +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fdd14e65b1c29e4e6df875fb5669ec00d6793531 diff --git a/external/imgui b/external/imgui deleted file mode 160000 index 41765fb..0000000 --- a/external/imgui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 41765fbda723d23e04e98afec40447d149d02ec8 diff --git a/external/portable-file-dialogs b/external/portable-file-dialogs deleted file mode 160000 index c12ea8c..0000000 --- a/external/portable-file-dialogs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c12ea8c9a727f5320a2b4570aee863bbede2a204 diff --git a/external/stb b/external/stb index 2fb8c5a..e6cd956 160000 --- a/external/stb +++ b/external/stb @@ -1 +1 @@ -Subproject commit 2fb8c5a3deb2110c89669f8d6f36e5833b556b44 +Subproject commit e6cd9561ea6dae43d41633797745789d142b691e diff --git a/resources/add_photo.png b/resources/add_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..1db8328735b74387c0d456bc7f70acf11ca7f636 GIT binary patch literal 898 zcmV-|1AY97P)@rHz!XPJ!`y zy*{lP;>Xy|sjC2X7OJa|0O-gZN_mQaLm6qE&J+PUGOF^TD8}r)sy$fVlWp>k`?#qM zw!hkzC(5X|t=-4xSG6^cfXQUC&hz{)4ZX#l?7?m1=~PuzZJ)2Uf$uRUr{k)R{2J%# z^T|G>MgWBP2lwv(mlYTR%x1H1a;1IB8v(3VtIzu+Sk__$AXf;Kzs<7jMrZWl9+T6( z&R>+{e2djmlmL3Y-UW81GX&Mdh%%l3|MH8PdJ3WhK)-^eXaKNni8IE|`?xEqXaFF@ zY9FVbQ;G?IlMvg=34krReW};g2&kPC7?9f5NSQHp5&-+U>YSk5=S>7U3BV+3m;0Ro z;Om^w832r`mvcgw0Kj1PWU7$>mx z0+40dyFAaYpUaS~`jM2kFGg-?F#5DuEWX%u(ij1R+_}wzL)T%Eti7jb(gz~|9xUdY z_akYO&gy(v95dZLQ@Ig#8c6ag`hPLsa5`uqa`0RR7-nxbm}000I_L_t&o Y0Cu3MpFTT4I{*Lx07*qoM6N<$g4nmCkN^Mx literal 0 HcmV?d00001 diff --git a/resources/center_focus.png b/resources/center_focus.png new file mode 100644 index 0000000000000000000000000000000000000000..967a73eec1bff199180fe42ac205da2a9b8d9373 GIT binary patch literal 984 zcmV;}11J26P)i+>5Vn9}CeOg%(BaN>c4t?PyQGTKC0!mNd4l8>QYD=yl~a<+?(EL)OvemA?~q~m z@wXZ@0|K;rr&}SAJhzr~x4+-+*?WS*QO19A05Vl7c%dhMH!)p1)x@|d10c?D?;bW5;FP=S-x4B6g)uYmShF2 zJ`4F2_LH9d#fV=l01>k660&%WjDBJnw=j?_bf{K>Dgel$apuEVK*w!(&gHWj0NBTO zt{lZyPCp7?Y5V=^f6yw3AuCN4f1J|ubeOo z0H;8*PL%-i!21Y_9LV}3@q#*$jE)u)0U+=dL7jm-e3*HR^TH48?|)dkr+dt`X$@IF z!(3t`Q2>Id@ISprMbjGC}X zhL3%Zzyg34HPP0T3CL)fx=c=h1pqDTe<-mz%|UE;16x9rI2Hg30Z4XG32$)Ep60-_ z>(Pb39u8F^Uqt{=L{te-?4RavUHi_!wmFiGL)-L5%paTB69ZtPbf0*00QMW>>C7u&pR%>d*SK@900030|EmpOivR!s21!IgR09B9FAbm3wkk0I00000}fG%{FVD3ru}QBUZNkBV5X|2G_n(Wy}J&I5thMrc4l6j5QTocZTzbP z*!Tu`>pP(DfQ=LAm%zq1K)(t0OQ2r@8z;~&0gVlCKA*oe>Xg)g=AmSLYHjrnxL&Wn zyWI|c{2)N=0L>${Q^9h_s{o7*zz}@dLx9L5dE>6{oqOxPO2G<*mX^AhHk zdJzB^NnZ0-5TN;T=BL3pKOM{YT?AklhRgkaA5sF^5Eu?Mk10KP`GKcXYJP591b_u0 z=mNnqJadQv@L+2Q*5R3<3IGdOLGTDq|J49kL>q!vczRa>z(QIOJj2ts8UPlnh2R~Y zo>c*`pcF!j@bs$=fJI9ovgM5r?SEE`{I~p59ylP$(CIc6e&fH8%iH6Q9q1uDi`mYR|a=Si0MXV7Mt_sr{R! zpyvVriDqAB1T&%cgkTP!G`u$iQvgfhdBa0~dy&+TqunO-9K@7k;ycYx! z0I%>K5JUiA?1C2k#-pwHAC=L9&%vdKCC+ncX%hi}*bPCyreYW~2_JNz8+~lv@AvOv zO=&LzKoOXEfnXh;nrzU0flw2k^~3;pFw8t4c!j4X8+0EKYQwWu6#y21nH!qu!wEcI z+Mt=d50%uc22dil#mN3rwr zP=3X!^K8HG{kD*DmrVRm08HEfi8w$UFmVF11tx9)IRs@3P+MTP+vU)*FH!Ri-QVee z{eG{qEIUH$zJxK4LnqlapA!HUs=5L_`f`jN4FoHI!{PAak>E9<(Lk^QKnAand2Q1) z-S`ND{X*t-hS1m9VjIVt%++=QXuMxl)l*RvD<92fvj?TrPh;RD$@@B6Y~x_7DX6jC zTJsP4f53b`FD($-YwG)1J59L;y~+Wu;9GtV1R-&qYl8#O&@=?xzkSXWDi-5!q00s5 zJzpM-Mi2mcew+!9K4%IQtIsVxPb~n;>$Z6h2EbfI$hH6oNk4|U#Q`x?A>=WPD*JF}ivzddd?Q+0ZD_{XA%P|S- zUqI&dZa&m)D*$}Ef2&{MaxaSF!Kqc4B1_Py`>_U+zJh{lHeZzRBw7r@x40!9^(p#qnems8i* z*Bg~eWe@E!+EW@o!#>q&wP*Cn5K`DBKr+>8^$;XGw26s{+fu3Y4FtE*K0&)i<7e2b zcDsEV+O)$zeKj&7V+0Wf2q2{wwN zcegQ5nF4#jKF;feGSwBOWp85b|8RG;Z z{IVQ#$32w*0DwvFtlWv3UB@ypq)F!_07`x;ZQ?Ybd&d$BG4=MFe76<=iDpppwLj0C{iTElkV z&)wciYhdMliP3$6kpNg*#)=vuy}%94dADKQ1OVC;)c;pf4gqkdac>#8|l1Q0a!DCr9pY?*KrXAAW6JWd(Sq{mc&6Tz~bWK zZl1{#dlCn&01SW|hy%8HG!yKJ0xT>nZ22aS7odmu0rRZe*ylTs3HC$*2nsJQ*4cWr zWz5b>w;}d2%;)jQB$$x^*hR~@mX=Kd_S=W~f%SZTe%|PL#z+7L#SfXjtAJsH7_hFI z1~wLe0kIUO*$%Q{=1VsCtOUSPs1LFTz;@4uJbkSM;DA9Df#iYB`tH?QfI$?2W$!|s zT$}{pKqRa)F?QU(T^*R47>LK!oCSyzV#i%a`DK2EU{2F2|6^K`&m%;_VZ7Xe}*n30zRm-GY{`&+jAjBRpzYZKS20C8eIfe|dqbUIB{V79+A z24lf?;`lz>AOgeyQKt2{DnOt_GEo%qA20Sv4u0G~%lr&GfIX#OY?$p0#(-b2Bd7qq zs7YpacJ?a>?vNbXbs9gzwqVD-V?uw=RDjTAT;h)ea8a#DV{%^sX(!_!00030|2Yy; iQ~&?~21!IgR09BO4BwEY@Ti;s0000Q-j+X=Iy6&;> z`=8T7QSYSyEPPc}-zn@p3qc0pD16F4-6;qX0B6Ea0_g#c;0`boKIJcpVHjSf7oZ1M zfED4N+qV4zWuFe~2A~k$1Y7`A!n*({g{K3kg{J~2hNl6jhNl21hlc~Ghlc_zg&)W9 z4SkVg0hb2kI$%xsrfEKwio3=9(lHN?Q@s%M5(S9AbNT6LZ10M#?Ysw+BxC5>9=+T;EL p00960RJnqY00006NklPo%gAO3V;fh2uk5m9~}TEnGrm~<9x3S7XTCl1lCtt%>V9i19-PU_UjUWEv!C& zc7Xa7V%L`)z!p}YKRZDE3bE_U4qywb&z~KjeudceWe2c@+4Fxo{{;X5|NkOpF7p5Y f00v1!K~w_(P3y{m(E(V!00000NkvXXu0mjfxRAs~ literal 0 HcmV?d00001 diff --git a/resources/pause.png b/resources/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..7a5b0dbbbfcde6df37b0b10fc488ccaac74733ac GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU^MV_aSW+od^6=BZ?gf9>%w9K zhT~;2YAJKRonUz5c=l>giu=Sx3H!O^onnsv``>zf%Aat$9}xB6V~}@T z#}KZtlPRV#jN#npKUT@#=c#@z;|!0@KN}Qh%y_+YudO2Q8lijXU$5*b-oxr$d6sDk zSO2fL@2w0`6LYdTPCVN&Pn>Cs(#>lE6V4Xe>a%3HZd$9TF#B;;9LI&OlxSy%lH+TC zDjW#X_+`i9Qhs9=Ka)=DqW3`IcC5lC?b**88={UbdTK6gaQ^doVWm5Y4{Q1S&srak z-Z165&Vfxj^65EC)xK1(-0r#K)vN#4*3D*JVOy}JhDGA+#Cv}o9P*F4Tk|uy6)!2b z7q}4p@#4AWhKn*U@1B3mnIZaypMl~3|0es75x^j11BD$k!->hO8ck;U{053Jc)I$z JtaD0e0syR2pCbSO literal 0 HcmV?d00001 diff --git a/resources/play.png b/resources/play.png new file mode 100644 index 0000000000000000000000000000000000000000..e322140f4d44b5d33e6e6013159c44487a9e54de GIT binary patch literal 651 zcmV;60(AX}P)5?Led8@H!JdBhg)=H~* zGm&NO#IoL}m7blGtmhvE(DMYyGy^mPdQL!>K+h9EH$hzjx&(SoK$ieV0?g<02abL@ z`eey~!C-K^TrR7!EH5|)?Xyo70L=i0!(j;!Urk#={eKLs0P34v0z!pyUAIM5ApkUj z-vQyLX-KI<0wB%jfKV+Ki+gHFD4_uu&3T?b1H=lOpjILxk5I))a@u$ zOaMkOvf&Qd5X~~9127bDKsH3NOfdk!fWE;kQyKt*KqZ)EN(Dd=f>@?x00be!Cdq1< z;sF>6S;l6WsQ@q>kPY4~GbsRu0uDYcGf4noKwmGGnKA%^KxMCG(hv5PV*o(<4t}XR z1^_pc$>fYE98!`607v+Q@Eqmi5aG==NdnkxHpO^6zG_5tsgnXwRn;qIJTHpkyNl@d zwW$DDW7bF0>D2pQY2pFwcDqN+`UtbWZqJ;rbIAaHAv~YWW>3Dv+WL_Spp&)Pgt}s) z0Qj;tmsnd|3;=9$-mJ|g)D?>kfWz8cVr}u50NAX}Ce#&+0)X4vTw-nU&;Wv1n@y-I z77{=ZYjX*8#n}PGur`-iTbva@ENim~b;Vc!Y`5DtdW^;MySKMCmw>Ku769DO?_EW= zuV(?!zJc^}1t5KfxW}g%z)e_s|C#~PXNY@zngQH|rT4EHAbp0o$CrEtbOrPW00960 ltvFXz00006Nkl literal 0 HcmV?d00001 diff --git a/resources/resources.qrc b/resources/resources.qrc new file mode 100644 index 0000000..21f745e --- /dev/null +++ b/resources/resources.qrc @@ -0,0 +1,18 @@ + + + center_focus.png + folder.png + navigate_before.png + navigate_next.png + pause.png + play.png + skip_next.png + skip_previous.png + zoom_in.png + zoom_out.png + close.png + save_alt.png + info.png + add_photo.png + + diff --git a/resources/save_alt.png b/resources/save_alt.png new file mode 100644 index 0000000000000000000000000000000000000000..f0e39d8e661dd43bcd5e0cfd6e6bd4b4323914a1 GIT binary patch literal 684 zcmV;d0#p5oP){AU+gA=;Glq zC;%jIg%G27_zMmIN%}&FRXn^!0f0od5MmY&U(o;{nFJwr@$eKC01|dVNKrieLsKDY8}1ppPJ6G(VXQDq*t@M{~^ z0Bi-vwoUx786vpWe0d9CWRyR3|!_Ljl&24h~Pd2r=*}2&{EOKtL9Q*;pf#4`#a#6Wb=rwtH zxjc@vytbFT@Atlh=Ax7MM*t*lfQ~po9FRBxnF5I$Ko&un0x|^>Cm>TGaRbP|Kyp*S zc6@69tJP{|cj_Z9isA-im3C`8R%-xAV5*Wrf>-z*-PZ*GExOz77R%-GLkAHZoB>dp zvn+dr8#d~9EX^qZBf19&CEV~}V%8@g0JLDZ;S(VKbinxF4S>;n4G1OY1=2SF%|R`Y z0P(E@W(N`gM)MsYY~Y5TiCLT|0Vu&exM70@@<|088Z`i|84Kh)-0-i1z=t3JjOI%~ zPz&VL#6eCW0cgRP5;K4x*-z*JtwV4CCZAyY2`&IM2e_fY0!g}`U2-6ID8%lF6Q@hg?UQ{#{Uzzfb7s;(@* zJ(U#YeG&kudOjQu?CT{X8^F!k-x`t0TApxz6wCoYporCxlZyPkB!nV#>(v0RIvSgYXEkKvBd%) zwu-sVR~+D)Xl(t717fR~>wLulu8GFhpEw}4in-2L9N?O0Z2gG?Vyl>)Z`=3)00960 l*&V0?00006Nklq$oZ^{o35)WXr+2l!*ydCoO-iQT&LN}pRFN$K~U3KY! zA^|}72MS$Y%CoUsQ2?;&J%}%$_7cHzi2<-&E+4S!ROMNOn@L0fz^ZT4G<`HlakfDL z!17awC(92wMVIFa1^~hru)OnGzAVP(o*e*I-P<|dMsRfuD*&vz*K>STj7^^n0K`*; z_j-=6Bly^FSOCEG-p}yIgw_3?1wfW%yU}RW$FcUMZem=&V*y~qjK|~K{eFLD`Vv}J z03;Z0_<}Md{aL}1O;FO4~N4H1@g}H zIa?6`NH7ZIGqyyZ?m0sY01}NYL4TLDC0?n|5uyMX!PpWvaKpLjH`$5=K!V|hFOC8! z763t@fE%blUeSFMAszsUMuB|CmY@Q;qrT5*1ppF^EkOm+`};&|01yNUxWViDMC$+$ z1i0bf_X+y`lyC+>mAV>8@bi=;Y=4V+o!=##0oW!lWt;+#vJ#>4PzQt}FJ)eJfRvR8 zm4`YY6nQE0ssp5~M5sK}0inoCnO7YkWhHF$a2@vt00960^=akv00006Nkl)bRGZz002ovPDHLkV1k$5FWdkC literal 0 HcmV?d00001 diff --git a/resources/zoom_in.png b/resources/zoom_in.png new file mode 100644 index 0000000000000000000000000000000000000000..c09cf5b83826e9f04e371e4c1cd353b53699e6ec GIT binary patch literal 1405 zcmV-@1%mpCP)9dyuXw{%ji+qu=Q)l#kMp~UfjBDL=W^=(9|%e$IF0C0YW zP(Sj1i+{l>AXHJWwFSWd66e>fHU7xhWUMb9A0N*lv#948CeKJ+S0}Pug<|Cg0l@ho zdOp{AoShyU8*3udBO@aZCMPFXk#(L|#>dAONF}d%dU|><&x!MjgdhMoZw*fzx+Biv z>}H;0DsN(9VoAyq6`z$(OC=KCX}Z#ndIjD9SgxMx#_E|~{|$#8sppWCsF(!a1B=ZK zqV5+Y$E>GdzBJYOB~7ODT0sXS44VA99Zc>!QNzA?R!p1b36o*tY@ zWXe>B(v6Oe{>A+vqF+PUR~7|c0BF%oBy-Vo&akHk*F**f2VZ6?&=e9)nyT}NePwfj z69DwDbXuYPLt|$(Rq02fsb!fIua&OcQh^gde}Dgy$(F{hoAhOMCnqO!W$OsBcLHE? z`rA-psY%aJ-u0g$l+ zz^blfu9#u9$eaPFa*XZVd{r(%^CPn%r~y*B{nf=|5AcHVarCyv+-2dxg2 zzlNRFc^h=v5$*B@AaA5MrX{tAoERVB&<2i~c8X_aveZ$o&5rWpnKFB`l0eVqMAQTU zkoN+iDQS*O;n;yVn8-KM4MgB{K9)R}x*N`O^-{G;M?3rI?MEDf`zbXiY07wIYFiaYCh9)wuQ|~-!=Cr9Q2!#RQd}oGcq7o3k z(I93fS;6rpdj*+jP5B*N6%a}RkXA8KwP^e>Ab#aJ*8%eN>oK;nQJPO@a4z^d{hYp! z4WTpu)i^*L;*5lF6BxrZ^cJBk8>m%M3F%dI%jC^bPMj7o{!bB(+n3pDTYo#Go@jGI zGyn>Oq`+wL!{}>5`Zmtakc#U}}o0hHtkajBMz6T(LRyUUI zML;+9YKTw02neCojb&>e0cAVoI{*Lx|Nl|=cLx9f00v1!K~w_(8_iUo7HX(w00000 LNkvXXu0mjfD&m1V literal 0 HcmV?d00001 diff --git a/resources/zoom_out.png b/resources/zoom_out.png new file mode 100644 index 0000000000000000000000000000000000000000..3e7bd8830dbe1fde687f1d0e5c4d9b86545c3286 GIT binary patch literal 1361 zcmV-X1+MyuP)9qkBcM?i}cXb*uF4*>U* zV5w9ZIXykyIypJ1Xyp9p=;*~=0q<-G_)d3scb6)a%0h&LeVv`1Z+tk%$H!4+Bh0wT zN(}(VKZ-LkPHS6RC=~o?d8jRvVMbyAgnEzX_%lcC+r{32BU{F15&^*Z2}1qS_$}rI z$AFN9U2O}j0eH^uX)XTGIApA^TwPtwAd{%)7$zS`D$0r5iUcbFIIj&)99kCVaP~0DF+Xp3 zcz8|96BWOFT_BZ6_!iKmeheBg2EcOlK3Lg*rq>_f&|Cl6CM7CHfw!TlGfULn)8si( z8v|gudain-I8U$tR^^%MfVYDE&#Ll7-MLEdN@oOs@%TmcLV9jFt@FO!fq{W1++QHU zt0&7SFakh}=8@1v&m{``kReYY(d6AWBt$`Gb3rTs==U|W#(SW#lisbfglH;jCdDad zF9{fm1<=*iwHByTeWbDb)f>nEU0+|%I5rt+E*1cj(^G|oRFj^O=qt$Iyht z9jX z^(=SFGeW)`Lldv9ZB_YL0POfSRTa<9&R(nZX>~-`R$3c2^*~4C8v|f2_{j@(=Y8`y zamjYrYIUIe6!o;u>!8yPXO}Smc_Y0sE~!T3#P|}24scAhQ>;~!rH*nHc9cJA%J9vO z2R)qQP-6u^-V20+q!BWPW9QYk`Ym>V2Q`ua7 z2Iqp?_0RS9t|6ob;5QBs7dRs!TmVKd4P79VWdl_tm5?r?TPJUZa^kdz@-Ia=ZeM4s zt^Mr~JI>|=X8?Q +#include + +AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) +{ + setWindowTitle("About"); + setFixedSize(380, 150); + + auto* lay = new QVBoxLayout(this); + lay->setContentsMargins(18, 18, 18, 18); + lay->setSpacing(0); + + auto* nameLbl = new QLabel("Sprite-Tools"); + nameLbl->setStyleSheet(QString("font-size: 17px; font-weight: bold; color: %1;").arg(SpriteColors::Accent.name())); + lay->addWidget(nameLbl); + lay->addSpacing(6); + + auto* descLbl = new QLabel("Sprite viewer and creator for Quake / Half-Life sprites"); + descLbl->setStyleSheet(QString("font-size: 12px; color: %1;").arg(SpriteColors::TextPrimary.name())); + descLbl->setWordWrap(true); + lay->addWidget(descLbl); + lay->addSpacing(16); + + + auto* linkLbl = new QLabel; + linkLbl->setTextFormat(Qt::RichText); + linkLbl->setOpenExternalLinks(true); + linkLbl->setText( + QString("GitHub: " + "" + "https://github.com/Elinsrc/Sprite-Tools") + .arg(SpriteColors::TextPrimary.name(), SpriteColors::Accent.name())); + linkLbl->setCursor(Qt::PointingHandCursor); + lay->addWidget(linkLbl); + + lay->addStretch(); +} \ No newline at end of file diff --git a/src/about_dialog.h b/src/about_dialog.h new file mode 100644 index 0000000..6ad719e --- /dev/null +++ b/src/about_dialog.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class AboutDialog : public QDialog +{ + Q_OBJECT +public: + explicit AboutDialog(QWidget* parent = nullptr); +}; \ No newline at end of file diff --git a/src/app_state.h b/src/app_state.h new file mode 100644 index 0000000..fb48678 --- /dev/null +++ b/src/app_state.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +struct AppState +{ + bool sprite_loaded = false; + std::string filepath; + std::string fileName; + int current_frame = 0; + int total_frames = 0; + + float zoom = 1.0f; + float scroll_x = 0.0f; + float scroll_y = 0.0f; + + bool animating = false; + float anim_speed = 1.0f; + double anim_time = 0.0; + + bool show_checker = true; + bool show_properties = true; + bool show_toolbar = true; + bool show_about = false; + bool show_export = false; + bool show_import = false; + + bool show_progress = false; + bool progress_done = false; + bool progress_success = false; + float progress_value = 0.0f; + std::string progress_title; + std::string progress_status; + std::string progress_result; + + std::string last_dir; + std::string status_msg; +}; \ No newline at end of file diff --git a/src/export_dialog.cpp b/src/export_dialog.cpp new file mode 100644 index 0000000..df450e5 --- /dev/null +++ b/src/export_dialog.cpp @@ -0,0 +1,151 @@ +#include "export_dialog.h" +#include "sprite_converter.h" +#include "theme.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ExportDialog::ExportDialog(QWidget* parent, AppState& state, SpriteLoader& loader) : QDialog(parent), m_state(state), m_loader(loader) +{ + setWindowTitle("Export Frames"); + setFixedSize(360, 280); + + auto* lay = new QVBoxLayout(this); + lay->setContentsMargins(24, 24, 24, 24); + lay->setSpacing(0); + + auto* fmtLbl = new QLabel("Output Format"); + fmtLbl->setStyleSheet(QString("font-size: 14px; font-weight: 500; color: %1;").arg(SpriteColors::TextSection.name())); + lay->addWidget(fmtLbl); + lay->addSpacing(8); + + m_fmtGroup = new QButtonGroup(this); + auto* fmtPng = new QRadioButton("PNG (with alpha)"); + auto* fmtBmp = new QRadioButton("BMP (no alpha)"); + fmtPng->setChecked(true); + m_fmtGroup->addButton(fmtPng, 0); + m_fmtGroup->addButton(fmtBmp, 1); + + lay->addWidget(fmtPng); + lay->addSpacing(4); + lay->addWidget(fmtBmp); + lay->addSpacing(20); + + auto* selLbl = new QLabel("Frame Selection"); + selLbl->setStyleSheet(QString("font-size: 14px; font-weight: 500; color: %1;").arg(SpriteColors::TextSection.name())); + lay->addWidget(selLbl); + lay->addSpacing(8); + + m_allFrames = new QRadioButton(QString("All frames (%1)").arg(state.total_frames)); + m_currentOnly = new QRadioButton(QString("Current frame (%1)").arg(state.current_frame + 1)); + m_allFrames->setChecked(true); + + lay->addWidget(m_allFrames); + lay->addSpacing(4); + lay->addWidget(m_currentOnly); + + auto* btnRow = new QHBoxLayout; + btnRow->setSpacing(8); + btnRow->addStretch(); + + auto* cancelBtn = new QPushButton("Cancel"); + cancelBtn->setCursor(Qt::PointingHandCursor); + cancelBtn->setStyleSheet(QString( + "QPushButton { background: transparent; color: %1; border: none; font-weight: 500; padding: 0 12px; height: 36px; }" + "QPushButton:hover { background: rgba(255, 255, 255, 0.05); border-radius: 4px; }" + ).arg(SpriteColors::Accent.name())); + + auto* exportBtn = new QPushButton("Export"); + exportBtn->setCursor(Qt::PointingHandCursor); + exportBtn->setStyleSheet(QString( + "QPushButton { background: %1; color: #FFFFFF; border: none; border-radius: 4px; font-weight: 500; padding: 0 16px; height: 36px; }" + "QPushButton:hover { background: %2; }" + ).arg(SpriteColors::Accent.name(), SpriteColors::AccentLit.name())); + + btnRow->addWidget(cancelBtn); + btnRow->addWidget(exportBtn); + lay->addLayout(btnRow); + + connect(exportBtn, &QPushButton::clicked, this, &ExportDialog::onExport); + connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); +} + +void ExportDialog::onExport() +{ + QSettings settings("Sprite-Tools"); + QString lastDir = settings.value("lastExportDir", QString::fromStdString(m_state.last_dir)).toString(); + + QString dir = QFileDialog::getExistingDirectory(this, "Output Directory", lastDir); + + if (dir.isEmpty()) + return; + + settings.setValue("lastExportDir", dir); + m_state.last_dir = dir.toStdString(); + + int format = m_fmtGroup->checkedId(); + int frameIdx = m_currentOnly->isChecked() ? m_state.current_frame : -1; + accept(); + + QProgressDialog prog("Exporting...", "Cancel", 0, 100, parentWidget()); + prog.setWindowTitle("Exporting Frames"); + prog.setWindowModality(Qt::WindowModal); + prog.setMinimumDuration(0); + + SpriteLoader loader; + if (!loader.Load(m_state.filepath)) + { + QMessageBox::critical(parentWidget(), "Error", "Failed to load sprite"); + return; + } + + QFileInfo fi(QString::fromStdString(m_state.filepath)); + QString prefix = fi.completeBaseName(); + int t = loader.GetTotalFrameCount(); + int start = 0, end = t; + if (frameIdx >= 0 && frameIdx < t) { start = frameIdx; end = frameIdx + 1; } + + int exported = 0; + std::string ext = SpriteConverter::GetFormatExtension(static_cast(format)); + + for (int i = start; i < end; i++) + { + if (prog.wasCanceled()) break; + prog.setLabelText(QString("Exporting frame %1 / %2...").arg(i + 1).arg(end)); + + SpriteFrame* frame = loader.GetFrame(i); + if (!frame || frame->rgba.empty()) + { + QMessageBox::critical(parentWidget(), "Error", QString("Failed to get frame %1").arg(i)); + return; + } + + QString outPath = (t == 1 && frameIdx < 0) + ? QString("%1/%2%3").arg(dir, prefix, QString::fromStdString(ext)) + : QString("%1/%2_%3%4").arg(dir, prefix).arg(i, 3, 10, QChar('0')).arg(QString::fromStdString(ext)); + + if (!SpriteConverter::SaveImageRGBA(outPath.toStdString().c_str(), + frame->rgba.data(), frame->width, frame->height, + static_cast(format))) + { + QMessageBox::critical(parentWidget(), "Error", "Failed to save: " + outPath); + return; + } + exported++; + prog.setValue((int)((float)(i - start + 1) / (end - start) * 100)); + QApplication::processEvents(); + } + + if (!prog.wasCanceled()) + QMessageBox::information(parentWidget(), "Export Complete", + QString("Exported %1 file(s) to:\n%2").arg(exported).arg(dir)); +} \ No newline at end of file diff --git a/src/export_dialog.h b/src/export_dialog.h new file mode 100644 index 0000000..7a30f6b --- /dev/null +++ b/src/export_dialog.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "app_state.h" +#include "sprite_loader.h" + +class ExportDialog : public QDialog +{ + Q_OBJECT +public: + ExportDialog(QWidget* parent, AppState& state, SpriteLoader& loader); + +private slots: + void onExport(); + +private: + AppState& m_state; + SpriteLoader& m_loader; + + QButtonGroup* m_fmtGroup; + QRadioButton* m_allFrames; + QRadioButton* m_currentOnly; +}; \ No newline at end of file diff --git a/src/frame_slider.cpp b/src/frame_slider.cpp new file mode 100644 index 0000000..4996c47 --- /dev/null +++ b/src/frame_slider.cpp @@ -0,0 +1,145 @@ +#include "frame_slider.h" +#include "theme.h" + +#include +#include +#include +#include + +FrameSlider::FrameSlider(QWidget* parent) : QWidget(parent) +{ + setMinimumHeight(28); + setMaximumHeight(28); + setMinimumWidth(80); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + setMouseTracking(true); +} + +void FrameSlider::setRange(int min, int max) +{ + m_min = min; + m_max = std::max(min, max); + m_value = std::clamp(m_value, m_min, m_max); + update(); +} + +void FrameSlider::setValue(int value) +{ + int clamped = std::clamp(value, m_min, m_max); + if (clamped != m_value) + { + m_value = clamped; + update(); + emit valueChanged(m_value); + } +} + +void FrameSlider::setEnabled(bool enabled) +{ + m_enabled = enabled; + QWidget::setEnabled(enabled); + update(); +} + +int FrameSlider::posToValue(int x) const +{ + if (m_max <= m_min) return m_min; + const int margin = 10; + int trackW = width() - margin * 2; + if (trackW <= 0) return m_min; + float ratio = std::clamp((float)(x - margin) / (float)trackW, 0.0f, 1.0f); + return m_min + (int)std::round(ratio * (m_max - m_min)); +} + +int FrameSlider::valueToPos(int val) const +{ + if (m_max <= m_min) return width() / 2; + const int margin = 10; + int trackW = width() - margin * 2; + float ratio = (float)(val - m_min) / (float)(m_max - m_min); + return margin + (int)(ratio * trackW); +} + +void FrameSlider::paintEvent(QPaintEvent*) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + int cy = height() / 2; + const int margin = 10; + int trackW = width() - margin * 2; + int steps = m_max - m_min; + + QColor trackBg = m_enabled ? SpriteColors::BgLight : SpriteColors::BgDark; + p.setPen(Qt::NoPen); + p.setBrush(trackBg); + p.drawRoundedRect(margin, cy - 2, trackW, 4, 2, 2); + + if (!m_enabled || steps <= 0) + return; + + int thumbX = valueToPos(m_value); + int activeW = thumbX - margin; + if (activeW > 0) + { + p.setBrush(SpriteColors::AccentDim); + p.drawRoundedRect(margin, cy - 2, activeW, 4, 2, 2); + } + + if (steps > 0 && steps <= 60) + { + float dotR = (steps <= 20) ? 3.0f : (steps <= 40) ? 2.0f : 1.5f; + + for (int i = 0; i <= steps; i++) + { + int dx = valueToPos(m_min + i); + bool active = (m_min + i) <= m_value; + + QColor dc = active ? SpriteColors::Accent : SpriteColors::TextDim; + dc.setAlpha(active ? 200 : 100); + + p.setBrush(dc); + p.drawEllipse(QPointF(dx, cy), dotR, dotR); + } + } + + p.setBrush(QColor(0, 0, 0, 40)); + p.drawEllipse(QPointF(thumbX, cy + 1), 8, 8); + + p.setBrush(SpriteColors::Accent); + p.drawEllipse(QPointF(thumbX, cy), 7, 7); +} + +void FrameSlider::mousePressEvent(QMouseEvent* event) +{ + if (!m_enabled || m_max <= m_min) return; + if (event->button() == Qt::LeftButton) + { + m_dragging = true; + int v = posToValue(event->pos().x()); + if (v != m_value) + { + m_value = v; + update(); + emit valueChanged(m_value); + } + } +} + +void FrameSlider::mouseMoveEvent(QMouseEvent* event) +{ + if (!m_dragging || !m_enabled) return; + int v = posToValue(event->pos().x()); + if (v != m_value) + { + m_value = v; + update(); + emit valueChanged(m_value); + } +} + +void FrameSlider::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + m_dragging = false; +} \ No newline at end of file diff --git a/src/frame_slider.h b/src/frame_slider.h new file mode 100644 index 0000000..0a68257 --- /dev/null +++ b/src/frame_slider.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +class FrameSlider : public QWidget +{ + Q_OBJECT + +public: + explicit FrameSlider(QWidget* parent = nullptr); + + void setRange(int min, int max); + void setValue(int value); + int value() const { return m_value; } + void setEnabled(bool enabled); + +signals: + void valueChanged(int value); + +protected: + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + +private: + int posToValue(int x) const; + int valueToPos(int val) const; + + int m_min = 0; + int m_max = 0; + int m_value = 0; + bool m_dragging = false; + bool m_enabled = true; +}; \ No newline at end of file diff --git a/src/icons/center_focus_strong.h b/src/icons/center_focus_strong.h deleted file mode 100644 index 063a926..0000000 --- a/src/icons/center_focus_strong.h +++ /dev/null @@ -1,85 +0,0 @@ -unsigned char center_focus_strong_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x03, - 0x8d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x5a, 0xbd, 0x72, 0xda, - 0x40, 0x10, 0xb6, 0x80, 0x61, 0x26, 0xcf, 0xc0, 0xdf, 0xd0, 0xe1, 0xce, - 0x74, 0x76, 0x67, 0x57, 0x8e, 0xbb, 0xa4, 0x8a, 0xd3, 0x25, 0x5d, 0x1e, - 0x21, 0x79, 0x82, 0xe4, 0x15, 0x52, 0x25, 0x9d, 0x27, 0x95, 0x53, 0x92, - 0xca, 0xee, 0xec, 0xce, 0xee, 0x4c, 0xc7, 0x0c, 0x3f, 0xef, 0x90, 0x61, - 0xf8, 0xf1, 0xb7, 0x1a, 0x34, 0x03, 0x02, 0xb4, 0x7b, 0xa7, 0x5b, 0x21, - 0x90, 0x3c, 0xb7, 0x96, 0x74, 0xb7, 0xbf, 0xdf, 0xee, 0xd9, 0x7b, 0x82, - 0xc2, 0x51, 0xc6, 0x7f, 0x72, 0x00, 0x32, 0x5e, 0x00, 0x47, 0x79, 0x05, - 0xe4, 0x15, 0x90, 0x71, 0x04, 0xf2, 0x2d, 0x90, 0xf1, 0x02, 0x30, 0xfb, - 0x23, 0x38, 0x18, 0x0c, 0x9e, 0x40, 0x73, 0x13, 0x8a, 0x0b, 0xb0, 0x89, - 0xad, 0x05, 0xef, 0x8b, 0x89, 0x4d, 0xd1, 0x16, 0xe8, 0xf5, 0x7a, 0xa7, - 0xa4, 0x1c, 0x8a, 0x4f, 0x40, 0x69, 0x1f, 0x2d, 0xf2, 0x75, 0x38, 0x1c, - 0x9e, 0x4a, 0x1c, 0x15, 0x01, 0x50, 0x2a, 0x95, 0x1e, 0x24, 0xca, 0xd2, - 0xc4, 0x33, 0x9f, 0xcf, 0x45, 0x3e, 0xb3, 0x00, 0x00, 0x4d, 0x91, 0xa2, - 0x34, 0x05, 0x1f, 0xf8, 0x02, 0xdf, 0x9f, 0x82, 0xfb, 0x6d, 0x57, 0x16, - 0x00, 0x08, 0x8a, 0x4a, 0x09, 0x7c, 0x69, 0x1c, 0xec, 0x96, 0x95, 0x00, - 0xb0, 0x16, 0x18, 0xca, 0xeb, 0xe3, 0x64, 0x32, 0x69, 0x4b, 0x68, 0x4d, - 0xd8, 0x70, 0x42, 0x62, 0x83, 0x78, 0xc8, 0x27, 0x43, 0xd5, 0x3e, 0xbb, - 0x15, 0x00, 0xd3, 0xe9, 0xb4, 0xdb, 0x6c, 0x36, 0x9f, 0x25, 0xe4, 0x5b, - 0x89, 0xf1, 0x4b, 0x62, 0x83, 0x78, 0xc8, 0x27, 0x1b, 0x33, 0x56, 0x00, - 0xd8, 0x18, 0x4a, 0x8b, 0x4c, 0xd8, 0x8f, 0x1c, 0x80, 0x30, 0x22, 0x59, - 0x7b, 0xce, 0x2b, 0x20, 0x6b, 0x19, 0x0f, 0xc7, 0x9b, 0x57, 0x40, 0x18, - 0x91, 0x24, 0x9e, 0x71, 0xb6, 0x68, 0x8d, 0x46, 0xa3, 0xef, 0xe8, 0xd4, - 0xee, 0xfa, 0xfd, 0x7e, 0x87, 0xee, 0x31, 0xc7, 0x36, 0x2d, 0x1a, 0xbe, - 0x25, 0x5a, 0x01, 0x38, 0xa0, 0x3c, 0x20, 0xe8, 0x39, 0xce, 0x16, 0x2f, - 0xb3, 0xd9, 0xec, 0x2b, 0x02, 0x3a, 0xf7, 0x3c, 0xef, 0x92, 0xee, 0x31, - 0xe7, 0x9f, 0x34, 0xc1, 0xc3, 0xb6, 0xaf, 0x90, 0x73, 0x36, 0x12, 0x01, - 0x00, 0x59, 0xbe, 0xa6, 0xc0, 0xd1, 0xad, 0xb1, 0x6d, 0x35, 0x78, 0x4e, - 0x88, 0x17, 0x32, 0xef, 0x9c, 0x45, 0x19, 0xa1, 0x88, 0x05, 0xa0, 0x56, - 0xab, 0x79, 0x61, 0xa2, 0xce, 0x2b, 0x42, 0xe7, 0xca, 0x12, 0x32, 0xfa, - 0x0b, 0x59, 0xbe, 0x59, 0x99, 0x14, 0x3c, 0x40, 0xe6, 0x96, 0x64, 0x05, - 0xac, 0x3e, 0x0b, 0xf9, 0x14, 0xf6, 0x93, 0x9e, 0xfd, 0xc5, 0x88, 0x5f, - 0x2c, 0x00, 0x11, 0xb2, 0xec, 0x12, 0xb2, 0x78, 0x8d, 0x8c, 0x7e, 0x62, - 0x19, 0xb7, 0x30, 0x90, 0x2c, 0x74, 0xa8, 0x56, 0x82, 0x2a, 0x00, 0xc8, - 0xa2, 0x71, 0xe6, 0xc3, 0x58, 0x40, 0xc7, 0x6d, 0x78, 0xce, 0xe5, 0xb3, - 0x1a, 0x00, 0xd8, 0xc7, 0x77, 0xae, 0x1c, 0xc5, 0x56, 0x50, 0x7b, 0x27, - 0xa1, 0x06, 0x00, 0x82, 0x3f, 0x07, 0x39, 0x19, 0xd8, 0x0a, 0xec, 0x1f, - 0x4f, 0x5b, 0x43, 0x2a, 0x00, 0x20, 0xfb, 0xce, 0x82, 0x0f, 0x02, 0xd3, - 0xea, 0x13, 0x54, 0x00, 0x28, 0x14, 0x0a, 0x97, 0x81, 0xe3, 0xae, 0xae, - 0xe5, 0x72, 0xf9, 0x83, 0x2b, 0x5d, 0xcb, 0x7a, 0x54, 0x00, 0xc0, 0xcb, - 0x89, 0xf6, 0xb2, 0x11, 0x17, 0xf7, 0xb6, 0x3a, 0x39, 0xdb, 0x2a, 0x00, - 0x70, 0x46, 0xd3, 0xb4, 0xae, 0x02, 0x40, 0xb1, 0x58, 0x74, 0xde, 0xce, - 0x6a, 0xe8, 0xa4, 0x44, 0xa8, 0x00, 0x30, 0x1e, 0x8f, 0xff, 0x90, 0x72, - 0x97, 0xa4, 0xa1, 0x93, 0xfc, 0x53, 0x01, 0x80, 0xda, 0x52, 0x52, 0xee, - 0x92, 0x34, 0x74, 0x92, 0x7f, 0x2a, 0x00, 0x90, 0x62, 0xd0, 0x3d, 0xc8, - 0xd5, 0x70, 0xa9, 0x6b, 0xc5, 0x27, 0x35, 0x00, 0x70, 0x10, 0xb9, 0x58, - 0xb1, 0x14, 0xe3, 0xc1, 0xa5, 0xae, 0xb0, 0x1b, 0x6a, 0x00, 0x2c, 0x0c, - 0x5d, 0x2d, 0xae, 0xd6, 0x17, 0x74, 0x81, 0xef, 0xad, 0x85, 0x05, 0x82, - 0xaa, 0x00, 0x20, 0x73, 0x1d, 0xbc, 0xec, 0xf8, 0x29, 0xf0, 0x63, 0x23, - 0x0b, 0x0e, 0x42, 0xbf, 0xeb, 0xf5, 0xfa, 0xdf, 0x8d, 0x8b, 0x8e, 0x26, - 0x59, 0x00, 0xd0, 0xd6, 0xae, 0x7d, 0x1f, 0xc0, 0xa4, 0x2d, 0x6d, 0x34, - 0x1a, 0x5f, 0xe0, 0xab, 0x4d, 0x25, 0x5c, 0x55, 0xab, 0xd5, 0xcf, 0x90, - 0x15, 0x0d, 0xf2, 0x69, 0x93, 0xaf, 0x9c, 0x30, 0x0b, 0x00, 0xa7, 0x40, - 0xb2, 0x4e, 0x95, 0x00, 0xf2, 0xc0, 0xfb, 0x08, 0x8a, 0x1c, 0xc8, 0xfa, - 0x23, 0xf1, 0x82, 0x3a, 0x91, 0x8c, 0x8e, 0x16, 0x13, 0x01, 0x20, 0xf0, - 0x15, 0x41, 0x9d, 0x81, 0x3c, 0x7c, 0x98, 0x79, 0x8c, 0xf3, 0xc2, 0x0f, - 0xec, 0xef, 0x7f, 0x58, 0xbb, 0xa7, 0x7b, 0xcc, 0xb5, 0x69, 0x0d, 0x59, - 0x3f, 0xc3, 0x5c, 0x62, 0x23, 0x51, 0x00, 0x82, 0xa8, 0xf0, 0x3f, 0xbd, - 0x5b, 0xa9, 0x54, 0xbe, 0x61, 0x7f, 0xbf, 0x45, 0xd0, 0x17, 0x74, 0x8f, - 0xb9, 0xe7, 0x60, 0x3d, 0xc9, 0xeb, 0x4e, 0x00, 0x48, 0x32, 0x40, 0xce, - 0x56, 0x0e, 0x00, 0x87, 0xd0, 0xa1, 0xaf, 0x1f, 0x5c, 0x05, 0x98, 0x26, - 0x2c, 0x07, 0xc0, 0x14, 0xb1, 0x43, 0xe3, 0xb7, 0xaa, 0x00, 0xbc, 0x9c, - 0x68, 0x51, 0xe7, 0x25, 0xa1, 0xb8, 0x80, 0x49, 0x6c, 0x10, 0x0f, 0xf9, - 0x64, 0x63, 0xcb, 0x0a, 0x00, 0x74, 0x6b, 0x37, 0xf4, 0x61, 0xa6, 0x84, - 0x6c, 0x9c, 0x5a, 0x96, 0x91, 0xd8, 0x20, 0x1e, 0xf2, 0x69, 0x59, 0x4e, - 0x7a, 0x2f, 0x01, 0x60, 0x27, 0x0d, 0x8a, 0x34, 0x00, 0x86, 0x8f, 0x7d, - 0x8f, 0xc0, 0x02, 0x80, 0x16, 0x35, 0xd1, 0xd6, 0x94, 0x09, 0xc8, 0x68, - 0x99, 0xba, 0x4c, 0x4e, 0x80, 0x05, 0x00, 0x2d, 0xea, 0x7f, 0x28, 0xb1, - 0x39, 0xcd, 0x41, 0x6c, 0x77, 0x03, 0x5b, 0x42, 0x94, 0x38, 0x16, 0x00, - 0x0a, 0x01, 0x48, 0x76, 0x50, 0x09, 0x6f, 0x70, 0xcf, 0x9e, 0xe6, 0xc0, - 0xb3, 0xeb, 0xd1, 0x85, 0xbf, 0x1e, 0x0e, 0x55, 0x22, 0x5f, 0x45, 0x00, - 0x50, 0x44, 0x54, 0x09, 0x50, 0xec, 0x9f, 0xe6, 0x70, 0x5d, 0xfb, 0xce, - 0xc0, 0xb6, 0x39, 0x92, 0x8d, 0x43, 0xdb, 0xf4, 0x46, 0xcc, 0x1f, 0x9b, - 0xd8, 0x13, 0x03, 0x60, 0xa2, 0x74, 0x9f, 0x78, 0x73, 0x00, 0xf6, 0x29, - 0x5b, 0x1a, 0xbe, 0xe6, 0x15, 0xa0, 0x81, 0xea, 0x3e, 0xe9, 0xcc, 0x2b, - 0x60, 0x9f, 0xb2, 0xb5, 0xc9, 0xd7, 0xb8, 0x73, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xab, 0x0d, 0x5f, 0x8b, 0x00, 0x00, 0x00, 0x06, 0x49, 0x44, - 0x41, 0x54, 0x03, 0x00, 0x5c, 0x2f, 0x0d, 0x9f, 0xd1, 0xb6, 0x2a, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int center_focus_strong_png_len = 984; diff --git a/src/icons/folder.h b/src/icons/folder.h deleted file mode 100644 index 02ca6b3..0000000 --- a/src/icons/folder.h +++ /dev/null @@ -1,50 +0,0 @@ -unsigned char folder_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x01, - 0xe1, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x95, 0xb1, 0x6e, 0x83, - 0x30, 0x10, 0x86, 0xc1, 0x02, 0xa9, 0x5b, 0x3b, 0x66, 0x00, 0x24, 0x6f, - 0xed, 0x96, 0xbc, 0x45, 0x1f, 0xa5, 0x5b, 0xc7, 0x8e, 0xed, 0x23, 0x74, - 0x6c, 0x9f, 0xa2, 0x7d, 0x93, 0x64, 0xec, 0x46, 0x10, 0x43, 0xd4, 0x27, - 0xe8, 0x82, 0xa0, 0x77, 0x99, 0xa2, 0x60, 0x5f, 0x6c, 0x41, 0x12, 0xcc, - 0x5d, 0xc4, 0x2f, 0x12, 0x7c, 0x36, 0xf7, 0x7f, 0xfe, 0x21, 0x2a, 0x62, - 0xfe, 0x11, 0x00, 0xcc, 0x03, 0x10, 0x49, 0x02, 0x24, 0x01, 0xcc, 0x09, - 0xc8, 0x23, 0xc0, 0x3c, 0x00, 0xf2, 0x12, 0x94, 0x47, 0xc0, 0xf5, 0x11, - 0x28, 0xcb, 0x72, 0x01, 0x5a, 0x0e, 0x91, 0xeb, 0xbd, 0x2e, 0x59, 0x47, - 0x26, 0x00, 0xcc, 0x2e, 0xea, 0xba, 0xee, 0x50, 0x49, 0x92, 0xec, 0x40, - 0xeb, 0x21, 0xaa, 0xaa, 0xea, 0xed, 0x92, 0xe6, 0x5c, 0xee, 0x65, 0x05, - 0x00, 0xe6, 0x97, 0x60, 0x76, 0xe7, 0xb2, 0x88, 0x6b, 0x8d, 0x52, 0xea, - 0x75, 0x6a, 0x10, 0xac, 0x00, 0xc0, 0xfc, 0xda, 0xd5, 0x98, 0x4f, 0xdd, - 0xd4, 0x20, 0x18, 0x01, 0x40, 0xe4, 0x1f, 0x7d, 0x4c, 0xf9, 0xd6, 0x4e, - 0x09, 0x82, 0x11, 0x40, 0xd7, 0x75, 0x4f, 0xbe, 0xa6, 0x7c, 0xeb, 0xaf, - 0x05, 0xe1, 0xb8, 0x4f, 0x23, 0x80, 0x38, 0x8e, 0x6f, 0x8f, 0x0b, 0xcf, - 0xf1, 0x1b, 0x21, 0x40, 0xda, 0xf6, 0x2f, 0xd9, 0x33, 0x9d, 0xdf, 0x4f, - 0xf5, 0x6d, 0x03, 0x70, 0x77, 0x6a, 0x62, 0x20, 0xe3, 0xcf, 0x08, 0x96, - 0xea, 0xd5, 0x08, 0x80, 0x9a, 0x10, 0xe2, 0x18, 0x40, 0xf8, 0xb3, 0xf5, - 0xcd, 0x02, 0x00, 0x98, 0xbf, 0xc1, 0xbf, 0x75, 0x38, 0xf7, 0x0e, 0x2e, - 0x00, 0xa2, 0x34, 0x4d, 0x5f, 0x7a, 0xee, 0xe1, 0x02, 0x1b, 0x00, 0xe0, - 0xf5, 0x1e, 0xd4, 0x3b, 0x38, 0x01, 0xe8, 0x99, 0xc7, 0x0b, 0x02, 0x00, - 0x29, 0x70, 0x96, 0x24, 0x80, 0xf3, 0xee, 0xa3, 0x77, 0x49, 0x00, 0x52, - 0xe0, 0x2c, 0x49, 0x00, 0xe7, 0xdd, 0x47, 0xef, 0xb3, 0x4f, 0x00, 0x9a, - 0xa4, 0x24, 0x00, 0x28, 0x3a, 0x1c, 0xc6, 0x24, 0x01, 0x1c, 0x76, 0x99, - 0xf2, 0x28, 0x09, 0xa0, 0xe8, 0x70, 0x18, 0x93, 0x04, 0x70, 0xd8, 0x65, - 0xca, 0xa3, 0x24, 0x80, 0xa2, 0xc3, 0x61, 0x4c, 0x12, 0xc0, 0x61, 0x97, - 0x29, 0x8f, 0x92, 0x00, 0x8a, 0x0e, 0x87, 0x31, 0x49, 0xc0, 0xdc, 0x76, - 0xd9, 0xd7, 0x8f, 0x31, 0x01, 0x6d, 0xdb, 0xfe, 0xfa, 0x2e, 0x34, 0xf5, - 0x7a, 0x9b, 0x27, 0x23, 0x00, 0x30, 0xf3, 0x01, 0x9a, 0xd5, 0xa1, 0x94, - 0xfa, 0x32, 0x19, 0x32, 0x02, 0x28, 0x8a, 0xe2, 0xdb, 0x54, 0x1c, 0xf2, - 0xb5, 0x2c, 0xcb, 0x3e, 0x4d, 0xfd, 0x1b, 0x01, 0x60, 0x61, 0xd3, 0x34, - 0x2b, 0x3c, 0xcf, 0x41, 0x94, 0x17, 0x2b, 0x00, 0xad, 0xf5, 0x06, 0x26, - 0xea, 0xd0, 0x01, 0xa0, 0x07, 0xf4, 0x62, 0xf3, 0x61, 0x05, 0x80, 0x13, - 0x60, 0xe2, 0x36, 0xcf, 0xf3, 0x18, 0x05, 0x0b, 0x3d, 0x80, 0x56, 0x81, - 0x48, 0x63, 0xcf, 0x28, 0xf4, 0x80, 0x5e, 0x6c, 0x22, 0x01, 0x1c, 0x4e, - 0x82, 0x85, 0x7e, 0x40, 0x9b, 0x40, 0xb4, 0x3d, 0xec, 0x9d, 0xfa, 0xee, - 0x0c, 0x80, 0x5a, 0x24, 0xe4, 0x31, 0x01, 0x10, 0xf2, 0xee, 0x8d, 0xd1, - 0xbb, 0x24, 0x60, 0x0c, 0x8a, 0x21, 0xaf, 0x21, 0x09, 0x08, 0x79, 0xf7, - 0xc6, 0xe8, 0x5d, 0x12, 0x30, 0x06, 0xc5, 0x6b, 0xae, 0x31, 0xf4, 0xde, - 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0xd2, 0x2d, 0x47, 0x00, 0x00, - 0x00, 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0x55, 0xba, 0xa5, 0x90, - 0xa1, 0x07, 0x34, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, - 0xae, 0x42, 0x60, 0x82 -}; -unsigned int folder_png_len = 556; diff --git a/src/icons/icons.h b/src/icons/icons.h deleted file mode 100644 index edc42a7..0000000 --- a/src/icons/icons.h +++ /dev/null @@ -1,10 +0,0 @@ -#include "center_focus_strong.h" -#include "folder.h" -#include "navigate_before.h" -#include "navigate_next.h" -#include "pause.h" -#include "play.h" -#include "skip_next.h" -#include "skip_previous.h" -#include "zoom_in.h" -#include "zoom_out.h" \ No newline at end of file diff --git a/src/icons/navigate_before.h b/src/icons/navigate_before.h deleted file mode 100644 index 3826694..0000000 --- a/src/icons/navigate_before.h +++ /dev/null @@ -1,45 +0,0 @@ -unsigned char navigate_before_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x01, - 0xa8, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0xd7, 0xcd, 0x4d, 0xc3, - 0x40, 0x10, 0x05, 0x60, 0x1b, 0xf9, 0x00, 0x37, 0x28, 0xc0, 0x96, 0x5c, - 0x06, 0x47, 0x6e, 0xb4, 0x40, 0x85, 0xd0, 0x01, 0x2d, 0xd0, 0x85, 0x0f, - 0x2e, 0x80, 0xdc, 0xc8, 0xc1, 0x92, 0xd9, 0xb1, 0x14, 0x29, 0x8a, 0x12, - 0xdb, 0x90, 0xbc, 0x37, 0xcf, 0xda, 0x89, 0xb2, 0xf9, 0xd1, 0x46, 0xde, - 0x79, 0x9f, 0x67, 0x13, 0xe7, 0xae, 0xc8, 0xfc, 0x16, 0x00, 0x99, 0x37, - 0x40, 0x11, 0x1d, 0x10, 0x1d, 0x90, 0xb9, 0x40, 0x6c, 0x81, 0xcc, 0x1b, - 0x20, 0xbe, 0x04, 0x63, 0x0b, 0xc4, 0x16, 0xc8, 0x5c, 0x20, 0xb6, 0x40, - 0xe6, 0x0d, 0x10, 0xbf, 0x02, 0xd9, 0x6d, 0x81, 0xd3, 0x8e, 0x97, 0x03, - 0xe8, 0xba, 0xee, 0xb1, 0xef, 0xfb, 0x9f, 0xd3, 0x42, 0x51, 0xef, 0xa5, - 0x00, 0x2c, 0x7c, 0x55, 0x55, 0xdf, 0x29, 0xec, 0x3d, 0x0b, 0x41, 0x06, - 0xe0, 0x28, 0x7c, 0xca, 0x3f, 0xdd, 0x29, 0x08, 0x12, 0x00, 0x67, 0xc2, - 0x4f, 0x02, 0xe9, 0x01, 0x8e, 0xe0, 0x0e, 0x30, 0x13, 0x3e, 0xe5, 0x2f, - 0x8a, 0x61, 0x18, 0x5e, 0xa6, 0x17, 0xa0, 0x07, 0x57, 0x80, 0x15, 0xe1, - 0x9f, 0xdb, 0xb6, 0xfd, 0x02, 0x65, 0x9f, 0x0e, 0xeb, 0x06, 0xa0, 0x10, - 0xde, 0x04, 0x5c, 0x00, 0x54, 0xc2, 0xbb, 0x00, 0x28, 0x85, 0xa7, 0x03, - 0xa8, 0x85, 0xa7, 0x02, 0x28, 0x86, 0xa7, 0x01, 0xa8, 0x86, 0xa7, 0x00, - 0x28, 0x87, 0x87, 0x03, 0xa8, 0x87, 0x87, 0x02, 0x2c, 0x85, 0x1f, 0xc7, - 0xf1, 0x0d, 0x7d, 0x91, 0x63, 0x01, 0x97, 0x06, 0xe4, 0x3a, 0x60, 0x4d, - 0xf8, 0xa6, 0x69, 0x3e, 0x96, 0x8a, 0xbb, 0xc5, 0xfc, 0xd2, 0x31, 0x20, - 0x00, 0x69, 0xd1, 0x7d, 0x1a, 0x17, 0xef, 0x65, 0x59, 0xee, 0x2e, 0x4e, - 0x92, 0x27, 0x20, 0x00, 0xa9, 0xb5, 0xf7, 0xe9, 0x4f, 0xcc, 0xc3, 0x4c, - 0x96, 0xcf, 0xf4, 0x7f, 0xff, 0x75, 0x66, 0x9e, 0x36, 0x05, 0x01, 0xb0, - 0xea, 0xb7, 0x82, 0x00, 0x03, 0xd8, 0x0a, 0x02, 0x14, 0x60, 0x0b, 0x08, - 0x70, 0x00, 0x75, 0x04, 0x0a, 0x80, 0x32, 0x02, 0x0d, 0x40, 0x15, 0x81, - 0x0a, 0xa0, 0x88, 0x40, 0x07, 0x50, 0x43, 0x70, 0x01, 0x50, 0x42, 0x70, - 0x03, 0x50, 0x41, 0x70, 0x05, 0x58, 0x89, 0xf0, 0x6e, 0x9f, 0x43, 0x0d, - 0x77, 0x00, 0x0b, 0x36, 0x73, 0xd9, 0xbc, 0xab, 0xeb, 0xfa, 0xc9, 0x3e, - 0x83, 0x1a, 0x12, 0x00, 0x16, 0xee, 0x0c, 0x02, 0x3c, 0xbc, 0xad, 0x2b, - 0x03, 0x60, 0xc5, 0x1c, 0x21, 0xfc, 0x3b, 0xbc, 0x1d, 0xe7, 0x2f, 0x43, - 0x0a, 0xc0, 0x0a, 0x37, 0x04, 0x74, 0xdb, 0xdb, 0x3a, 0x87, 0x21, 0x07, - 0x70, 0x28, 0x8c, 0xf5, 0x1c, 0x00, 0x2c, 0x69, 0xd5, 0x75, 0xa2, 0x03, - 0x54, 0xcf, 0x0c, 0xab, 0xae, 0xe8, 0x00, 0x96, 0xb4, 0xea, 0x3a, 0xd1, - 0x01, 0xaa, 0x67, 0x86, 0x55, 0x57, 0x74, 0x00, 0x4b, 0x5a, 0x75, 0x9d, - 0xe8, 0x00, 0xd5, 0x33, 0xc3, 0xaa, 0x2b, 0x3a, 0x80, 0x25, 0x8d, 0x5a, - 0xe7, 0xda, 0xe3, 0xfe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x54, 0xb9, 0x82, - 0x91, 0x00, 0x00, 0x00, 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0x07, - 0xde, 0xed, 0x81, 0xdd, 0xcd, 0x75, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x49, - 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int navigate_before_png_len = 499; diff --git a/src/icons/navigate_next.h b/src/icons/navigate_next.h deleted file mode 100644 index c9dc50e..0000000 --- a/src/icons/navigate_next.h +++ /dev/null @@ -1,44 +0,0 @@ -unsigned char navigate_next_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x01, - 0x9e, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0xd8, 0x3d, 0x6e, 0xc2, - 0x40, 0x10, 0x05, 0x60, 0x1c, 0xb9, 0x48, 0x09, 0x07, 0x70, 0xe1, 0x9b, - 0xa4, 0x4d, 0x97, 0x36, 0xa7, 0xa4, 0xa5, 0xe3, 0x28, 0x96, 0xec, 0x03, - 0xc0, 0x01, 0x2c, 0x99, 0x79, 0x48, 0x6e, 0x8c, 0x41, 0xfc, 0xe8, 0xcd, - 0x3c, 0x69, 0x27, 0xf2, 0x06, 0x01, 0x62, 0x3d, 0xef, 0xf3, 0x2c, 0x2c, - 0x7c, 0x6d, 0x0a, 0xff, 0x4b, 0x80, 0xc2, 0x1b, 0x60, 0x93, 0x1d, 0x90, - 0x1d, 0x50, 0xb8, 0x40, 0x2e, 0x81, 0xc2, 0x1b, 0x20, 0xdf, 0x04, 0x73, - 0x09, 0xe4, 0x12, 0x28, 0x5c, 0x20, 0x97, 0x40, 0xe1, 0x0d, 0x90, 0x9f, - 0x02, 0xc5, 0x2d, 0x81, 0x65, 0xc7, 0xbb, 0x01, 0x0c, 0xc3, 0x70, 0xea, - 0xba, 0x6e, 0xbb, 0x2c, 0x20, 0xfa, 0xbe, 0x0b, 0x00, 0xc2, 0x5b, 0xd0, - 0x6d, 0x5d, 0xd7, 0x72, 0x08, 0x74, 0x80, 0x39, 0xbc, 0x01, 0x5c, 0x0f, - 0x35, 0x04, 0x2a, 0xc0, 0x32, 0xfc, 0x55, 0xc0, 0xfe, 0x29, 0x21, 0x50, - 0x01, 0x2c, 0xeb, 0xbf, 0x8d, 0xd5, 0x43, 0x05, 0x81, 0x0a, 0xd0, 0x34, - 0xcd, 0xc1, 0xd2, 0xff, 0xda, 0x58, 0x3d, 0x14, 0x10, 0xa8, 0x00, 0x48, - 0xad, 0x8e, 0x40, 0x07, 0x50, 0x47, 0x70, 0x01, 0x50, 0x46, 0x70, 0x03, - 0x50, 0x45, 0x70, 0x05, 0x50, 0x44, 0x70, 0x07, 0x50, 0x43, 0x08, 0x01, - 0x50, 0x42, 0x08, 0x03, 0x50, 0x41, 0x08, 0x05, 0x50, 0x40, 0x08, 0x07, - 0x60, 0x23, 0x60, 0xfe, 0x47, 0x43, 0x02, 0x00, 0x05, 0x4e, 0xd3, 0xf4, - 0x8d, 0x5b, 0xef, 0x21, 0x01, 0xd0, 0xf7, 0xfd, 0x5f, 0x55, 0x55, 0xfb, - 0x7b, 0xe1, 0xc7, 0x71, 0xdc, 0xb5, 0x6d, 0x7b, 0xbe, 0xf7, 0xfc, 0x27, - 0x8f, 0x87, 0x03, 0xd8, 0xaf, 0x44, 0x3f, 0x55, 0x50, 0x78, 0xc0, 0x85, - 0x02, 0x20, 0xbc, 0x7d, 0x23, 0x3c, 0xa2, 0x90, 0xb5, 0xc1, 0xbc, 0xf2, - 0xf3, 0xf9, 0xc2, 0x00, 0x14, 0xc2, 0x03, 0x21, 0x04, 0x40, 0x25, 0x7c, - 0x08, 0x80, 0x52, 0x78, 0x77, 0x00, 0xb5, 0xf0, 0xae, 0x00, 0x8a, 0xe1, - 0xdd, 0x00, 0x54, 0xc3, 0xbb, 0x00, 0x28, 0x87, 0xa7, 0x03, 0xa8, 0x87, - 0xa7, 0x03, 0xd8, 0x26, 0x27, 0x64, 0x7b, 0x8b, 0x60, 0xcf, 0x0e, 0xea, - 0x3e, 0xc0, 0x7e, 0x12, 0xdf, 0x59, 0x21, 0x37, 0x7b, 0x78, 0x8f, 0x1d, - 0x9e, 0x9d, 0xf7, 0xa9, 0x83, 0x0a, 0x80, 0x0a, 0x96, 0x08, 0x4a, 0xe1, - 0x51, 0x1f, 0x1d, 0x00, 0x27, 0x99, 0x11, 0x3c, 0xc2, 0xe3, 0x7c, 0xaf, - 0x0c, 0x17, 0x00, 0x14, 0x04, 0x04, 0xd6, 0x57, 0x5a, 0xcc, 0xff, 0xee, - 0x70, 0x03, 0x78, 0xb7, 0x40, 0xf6, 0xeb, 0x12, 0x80, 0x2d, 0xac, 0x3e, - 0x7f, 0x76, 0x80, 0xfa, 0x15, 0x62, 0xd7, 0x97, 0x1d, 0xc0, 0x16, 0x56, - 0x9f, 0x3f, 0x3b, 0x40, 0xfd, 0x0a, 0xb1, 0xeb, 0xcb, 0x0e, 0x60, 0x0b, - 0xab, 0xcf, 0x9f, 0x1d, 0xa0, 0x7e, 0x85, 0xd8, 0xf5, 0x65, 0x07, 0xb0, - 0x85, 0xd9, 0xf3, 0x7f, 0x3a, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x22, - 0x66, 0x2e, 0xf3, 0x00, 0x00, 0x00, 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, - 0x00, 0x4d, 0xeb, 0xca, 0x81, 0xd1, 0x01, 0x58, 0xbd, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int navigate_next_png_len = 489; diff --git a/src/icons/pause.h b/src/icons/pause.h deleted file mode 100644 index 11c6ddf..0000000 --- a/src/icons/pause.h +++ /dev/null @@ -1,35 +0,0 @@ -unsigned char pause_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x01, - 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x94, 0xc1, 0x0d, 0x83, - 0x30, 0x0c, 0x45, 0xa1, 0x73, 0x30, 0x00, 0xc7, 0x76, 0x1c, 0x26, 0x64, - 0x9c, 0xf6, 0xc8, 0x00, 0xec, 0x41, 0xcd, 0xd5, 0x52, 0x64, 0x47, 0x91, - 0xa2, 0x60, 0xbf, 0x0a, 0x1f, 0x42, 0x5c, 0xc7, 0xfe, 0xff, 0x85, 0xd7, - 0x94, 0xfc, 0x87, 0x00, 0xc9, 0x01, 0x98, 0x20, 0x00, 0x02, 0x92, 0x2b, - 0xc0, 0x15, 0x48, 0x0e, 0x00, 0x1f, 0x41, 0xae, 0x00, 0x57, 0x20, 0xb9, - 0x02, 0x5c, 0x81, 0x56, 0x00, 0xce, 0xf3, 0xfc, 0x3a, 0x63, 0xf7, 0x9e, - 0x25, 0xf5, 0x76, 0x09, 0x57, 0x5d, 0x6f, 0xcd, 0x52, 0x5e, 0x33, 0x01, - 0xd7, 0x75, 0xbd, 0x3d, 0x21, 0x0d, 0xac, 0x12, 0xde, 0x67, 0xf5, 0xd4, - 0xbc, 0x73, 0xbc, 0x05, 0x4b, 0x79, 0xcd, 0x02, 0x94, 0x0a, 0x8f, 0xfa, - 0x5e, 0xf7, 0x85, 0x00, 0x5a, 0x91, 0x6c, 0x6b, 0x08, 0xc8, 0xe6, 0xb8, - 0x9e, 0x17, 0x02, 0xb4, 0x22, 0xd9, 0xd6, 0x10, 0x90, 0xcd, 0x71, 0x3d, - 0x2f, 0x04, 0x68, 0x45, 0xb2, 0xad, 0x21, 0x20, 0x9b, 0xe3, 0x7a, 0x5e, - 0x08, 0xd0, 0x8a, 0x64, 0x5b, 0x43, 0x40, 0x74, 0xc7, 0xad, 0xf9, 0x20, - 0xc0, 0x52, 0x28, 0xfa, 0x3e, 0x04, 0x44, 0x77, 0xd8, 0x9a, 0x0f, 0x02, - 0x2c, 0x85, 0xa2, 0xef, 0x43, 0x40, 0x74, 0x87, 0xad, 0xf9, 0x20, 0xc0, - 0x52, 0x28, 0xfa, 0x3e, 0x04, 0x44, 0x77, 0xd8, 0x9a, 0x0f, 0x02, 0x2c, - 0x85, 0xa2, 0xef, 0x43, 0x40, 0x34, 0x87, 0x6b, 0xe7, 0x81, 0x80, 0x5a, - 0xc5, 0xa2, 0xe5, 0x37, 0x13, 0x30, 0xcf, 0xf3, 0xcf, 0x13, 0x22, 0xdc, - 0x21, 0xe1, 0x7d, 0x0e, 0x4f, 0xcd, 0x3b, 0xc7, 0x5b, 0xb0, 0x94, 0xd7, - 0x2c, 0xc0, 0xb2, 0x2c, 0x1f, 0x67, 0x6c, 0xa5, 0x26, 0xf4, 0x7b, 0xa9, - 0xb7, 0x49, 0xb8, 0xea, 0xea, 0xff, 0xd6, 0xae, 0x9b, 0x05, 0xa8, 0x3d, - 0x70, 0xb4, 0x7c, 0x04, 0x18, 0xcd, 0x91, 0xde, 0xfd, 0x40, 0x40, 0x6f, - 0xc5, 0x47, 0x3b, 0x0f, 0x02, 0x46, 0x73, 0xa4, 0x77, 0x3f, 0x10, 0xd0, - 0x5b, 0xf1, 0xd1, 0xce, 0x83, 0x80, 0xd1, 0x1c, 0xe9, 0xdd, 0xcf, 0xe3, - 0x09, 0x68, 0x15, 0xec, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x82, 0x3f, 0xf1, - 0x58, 0x00, 0x00, 0x00, 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0xc8, - 0x93, 0xaa, 0x81, 0x34, 0x99, 0x4c, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x49, - 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int pause_png_len = 379; diff --git a/src/icons/play.h b/src/icons/play.h deleted file mode 100644 index 1f9ad63..0000000 --- a/src/icons/play.h +++ /dev/null @@ -1,58 +0,0 @@ -unsigned char play_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x02, - 0x40, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x98, 0x3d, 0x52, 0xc3, - 0x30, 0x10, 0x85, 0x6d, 0x92, 0x82, 0x94, 0x74, 0x14, 0xf9, 0xc1, 0x1d, - 0x57, 0xe0, 0x06, 0x70, 0x0a, 0x4a, 0x28, 0xe1, 0x26, 0x74, 0x40, 0xc9, - 0x29, 0xe0, 0x08, 0xdc, 0x80, 0xd2, 0x24, 0x29, 0x68, 0xe9, 0x92, 0x22, - 0x89, 0x79, 0xab, 0xb1, 0x67, 0x3c, 0x8c, 0x94, 0x22, 0xd6, 0x4a, 0xab, - 0x78, 0x33, 0x91, 0x65, 0xed, 0xc4, 0xb2, 0xde, 0xa7, 0x95, 0x9e, 0x9d, - 0x93, 0xac, 0xe7, 0x1f, 0x05, 0xd0, 0xf3, 0x04, 0xc8, 0x34, 0x03, 0x34, - 0x03, 0x7a, 0x4e, 0x40, 0x97, 0x40, 0xcf, 0x13, 0x40, 0x37, 0x41, 0x5d, - 0x02, 0xba, 0x04, 0x7a, 0x4e, 0x40, 0x97, 0x00, 0x47, 0x02, 0xcc, 0xe7, - 0xf3, 0x07, 0x8e, 0x7e, 0x39, 0xfa, 0x64, 0xc9, 0x80, 0xc1, 0x60, 0x70, - 0xbb, 0x5c, 0x2e, 0xab, 0xb2, 0x2c, 0x2f, 0x38, 0x06, 0xed, 0xb3, 0x4f, - 0x16, 0x00, 0xcd, 0x00, 0x87, 0xc3, 0x61, 0x09, 0x10, 0x5f, 0x4d, 0x5b, - 0x42, 0xfd, 0x7f, 0x0c, 0xac, 0x00, 0xea, 0x9b, 0x5d, 0x02, 0x42, 0x85, - 0x72, 0x5d, 0xb7, 0x45, 0x55, 0x21, 0x00, 0x34, 0x82, 0xdf, 0x01, 0xe1, - 0xa7, 0x69, 0x48, 0xa9, 0x43, 0x02, 0x20, 0xcd, 0xe7, 0x80, 0x50, 0x2d, - 0x16, 0x8b, 0x7b, 0x6a, 0x48, 0x28, 0xa1, 0x01, 0x18, 0xcd, 0x79, 0x9e, - 0x3f, 0x03, 0xc4, 0x0a, 0x9b, 0xe4, 0xa9, 0x09, 0x44, 0x3c, 0x44, 0x01, - 0x50, 0xeb, 0x3d, 0xc5, 0x26, 0xb9, 0x42, 0x36, 0xbc, 0xd4, 0xed, 0x28, - 0x55, 0x4c, 0x00, 0x46, 0x30, 0xb2, 0xe1, 0x0e, 0xd9, 0x10, 0xcd, 0x32, - 0xa3, 0x03, 0x30, 0x14, 0x70, 0x40, 0x36, 0x44, 0xb1, 0x4c, 0x31, 0x00, - 0xc0, 0x80, 0xbe, 0xc1, 0x2d, 0x53, 0x1a, 0x00, 0x82, 0x40, 0x25, 0x98, - 0x65, 0x4a, 0x05, 0x40, 0x10, 0x82, 0x58, 0xa6, 0x64, 0x00, 0x04, 0x21, - 0xc3, 0x26, 0xc9, 0x6a, 0x99, 0xe2, 0x01, 0x18, 0x0a, 0x59, 0xc6, 0x66, - 0x99, 0xa9, 0x00, 0x30, 0x1c, 0x90, 0x0d, 0xde, 0x2d, 0x33, 0x29, 0x00, - 0x86, 0x02, 0x0e, 0x3e, 0x2d, 0x33, 0x49, 0x00, 0x60, 0x40, 0x5f, 0x2f, - 0x96, 0x99, 0x32, 0x00, 0x82, 0x40, 0x65, 0xaf, 0x65, 0xd2, 0x0f, 0xf6, - 0x95, 0x63, 0x00, 0x40, 0xfa, 0x0e, 0x7e, 0xa9, 0x3a, 0x06, 0x00, 0x37, - 0x93, 0xc9, 0xe4, 0x8c, 0x28, 0x1c, 0x52, 0x92, 0x05, 0x00, 0x47, 0xf8, - 0x84, 0xf0, 0x1c, 0xe5, 0xe3, 0x10, 0xe1, 0xcd, 0x35, 0x49, 0x02, 0xd8, - 0x6c, 0x36, 0xc5, 0x78, 0x3c, 0xbe, 0x6a, 0x44, 0x74, 0xa9, 0x93, 0x02, - 0x50, 0x55, 0xd5, 0x2b, 0x66, 0x3c, 0x2f, 0x8a, 0xe2, 0xbb, 0x8b, 0xe8, - 0xf6, 0xb5, 0xa9, 0x00, 0x58, 0x63, 0xd6, 0x47, 0xd3, 0xe9, 0xd4, 0xfb, - 0x5f, 0x69, 0xe2, 0x01, 0xec, 0x76, 0xbb, 0x47, 0xcc, 0xfa, 0x08, 0xb3, - 0xbe, 0x6e, 0xcf, 0x9c, 0xaf, 0x73, 0xc9, 0x00, 0x7e, 0x21, 0x3c, 0x9f, - 0xcd, 0x66, 0x4f, 0xbe, 0xc4, 0xda, 0xfa, 0x91, 0x0a, 0xa0, 0x93, 0xb5, - 0xd9, 0x84, 0xba, 0x62, 0xa2, 0x00, 0xf8, 0xb2, 0x36, 0x97, 0x58, 0x5b, - 0x5c, 0x0c, 0x00, 0x6c, 0x72, 0xde, 0xac, 0xcd, 0x26, 0xd4, 0x15, 0x8b, - 0x0e, 0x80, 0xc3, 0xda, 0x5c, 0x62, 0x6d, 0xf1, 0x98, 0x00, 0xd8, 0xac, - 0xcd, 0x26, 0xd4, 0x15, 0x8b, 0x02, 0x80, 0xdb, 0xda, 0x5c, 0x62, 0x6d, - 0xf1, 0xd0, 0x00, 0x82, 0x58, 0x9b, 0x4d, 0xa8, 0x2b, 0x16, 0x12, 0x40, - 0x10, 0x6b, 0x73, 0x09, 0x75, 0xc5, 0xd9, 0x01, 0xc4, 0xb0, 0x36, 0x97, - 0x58, 0x5b, 0x9c, 0x15, 0x40, 0x2c, 0x6b, 0xb3, 0x09, 0x75, 0xc5, 0x58, - 0x00, 0x6c, 0xb7, 0xdb, 0x37, 0x7a, 0x8c, 0xc5, 0xf3, 0xbb, 0xb7, 0xb7, - 0x36, 0x97, 0x80, 0xae, 0x71, 0x16, 0x00, 0xdc, 0xcf, 0xef, 0x5d, 0x45, - 0xb7, 0xaf, 0x67, 0x01, 0xd0, 0xbe, 0x81, 0xf4, 0x73, 0x05, 0x20, 0x7d, - 0x86, 0xb8, 0xc7, 0xa7, 0x19, 0xc0, 0x4d, 0x58, 0x7a, 0xff, 0x9a, 0x01, - 0xd2, 0x67, 0x88, 0x7b, 0x7c, 0x9a, 0x01, 0xdc, 0x84, 0xa5, 0xf7, 0xaf, - 0x19, 0x20, 0x7d, 0x86, 0xb8, 0xc7, 0x97, 0x7c, 0x06, 0x74, 0x05, 0xf4, - 0x07, 0x00, 0x00, 0xff, 0xff, 0xad, 0x38, 0x57, 0x55, 0x00, 0x00, 0x00, - 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0x16, 0x98, 0xa6, 0x81, 0x52, - 0xf2, 0x22, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, - 0x42, 0x60, 0x82 -}; -unsigned int play_png_len = 651; diff --git a/src/icons/skip_next.h b/src/icons/skip_next.h deleted file mode 100644 index 662b23f..0000000 --- a/src/icons/skip_next.h +++ /dev/null @@ -1,58 +0,0 @@ -unsigned char skip_next_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x02, - 0x40, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x97, 0xa1, 0x73, 0xc2, - 0x30, 0x14, 0xc6, 0xe9, 0x0e, 0x01, 0x6e, 0x78, 0x38, 0x56, 0x37, 0x39, - 0x8b, 0xc3, 0xce, 0xcd, 0xce, 0xcd, 0x6d, 0x72, 0xfb, 0x4f, 0x36, 0xb5, - 0xb9, 0xd9, 0xb9, 0xd9, 0x39, 0x2c, 0x72, 0x6e, 0xb2, 0x1c, 0xfc, 0x01, - 0xc3, 0x81, 0xe0, 0x28, 0x5f, 0x72, 0x51, 0xb9, 0x52, 0xe8, 0x35, 0x79, - 0x79, 0xb9, 0x3c, 0x8e, 0xb4, 0xbc, 0xb6, 0x97, 0xbc, 0xef, 0xf7, 0xbe, - 0x84, 0xe6, 0xa2, 0x93, 0xf8, 0x47, 0x00, 0x24, 0x6e, 0x80, 0x8e, 0x38, - 0x40, 0x1c, 0x90, 0x38, 0x01, 0x99, 0x02, 0x89, 0x1b, 0x40, 0x16, 0x41, - 0x99, 0x02, 0x32, 0x05, 0x12, 0x27, 0x20, 0x53, 0x20, 0x71, 0x03, 0xc8, - 0xbf, 0x40, 0x72, 0x53, 0xc0, 0x76, 0x7c, 0x6b, 0x00, 0xab, 0xd5, 0x6a, - 0x66, 0x77, 0xea, 0x23, 0x2e, 0x8a, 0xe2, 0x06, 0x63, 0x95, 0x76, 0x6b, - 0x3b, 0x56, 0x6b, 0x00, 0x48, 0x60, 0xaa, 0x92, 0x42, 0x82, 0x57, 0xf8, - 0x1d, 0xdd, 0xd7, 0x05, 0x00, 0x2d, 0xba, 0xdb, 0xed, 0x16, 0xcb, 0xe5, - 0xf2, 0x43, 0x07, 0x11, 0x1d, 0x9c, 0x01, 0x50, 0x9a, 0xb3, 0x2c, 0x7b, - 0x84, 0x1b, 0x36, 0xea, 0x77, 0x2c, 0xcd, 0x29, 0x00, 0x23, 0xba, 0x07, - 0x08, 0x25, 0xdc, 0xf0, 0x60, 0x62, 0xd6, 0x27, 0x1f, 0x00, 0xb4, 0x60, - 0xb8, 0xe1, 0x13, 0x20, 0xfe, 0x74, 0xc0, 0xf8, 0xe0, 0x0d, 0x80, 0xd1, - 0x7c, 0x0d, 0x08, 0x25, 0xe7, 0x05, 0xd2, 0x37, 0x00, 0xcd, 0x41, 0x2d, - 0x90, 0x00, 0xf1, 0xad, 0x03, 0x66, 0x07, 0x12, 0x00, 0x46, 0xf3, 0x1d, - 0x20, 0x6c, 0xe0, 0x86, 0x9e, 0x89, 0x59, 0x9c, 0x28, 0x01, 0x28, 0xc1, - 0x3d, 0xb8, 0x61, 0x83, 0x05, 0xf2, 0x49, 0x05, 0x1c, 0x1a, 0x35, 0x00, - 0xad, 0x19, 0x0b, 0xe4, 0x3b, 0xdc, 0xf0, 0xaf, 0x83, 0xc0, 0x87, 0x20, - 0x00, 0x8c, 0xe6, 0x4b, 0x40, 0x50, 0x0b, 0xe4, 0xd4, 0xc4, 0x41, 0x4e, - 0x21, 0x01, 0x68, 0xc1, 0x98, 0x12, 0x33, 0x80, 0x20, 0xd9, 0x4f, 0xe8, - 0x01, 0xad, 0x43, 0x70, 0x00, 0x26, 0x9f, 0x60, 0xfb, 0x09, 0x2e, 0x00, - 0x34, 0x07, 0xb8, 0xa1, 0xc0, 0x02, 0x49, 0xba, 0x9f, 0x60, 0x05, 0x40, - 0x51, 0xc0, 0x02, 0xe9, 0x74, 0x3f, 0xa1, 0xfa, 0xac, 0x6b, 0xec, 0x00, - 0xd4, 0x25, 0xeb, 0xe3, 0x1e, 0x47, 0x00, 0x6f, 0xa3, 0xd1, 0xa8, 0xef, - 0x43, 0x6c, 0x55, 0x9f, 0xac, 0x00, 0xec, 0x76, 0xbb, 0x01, 0xc4, 0x3f, - 0x57, 0x25, 0xea, 0xeb, 0x1a, 0x17, 0x00, 0x73, 0x08, 0xcf, 0xf2, 0x3c, - 0x5f, 0xfb, 0x12, 0x7a, 0xac, 0xdf, 0xe0, 0x00, 0x50, 0xf5, 0x09, 0xc4, - 0x4f, 0x8e, 0x25, 0xe8, 0xfb, 0x7a, 0x48, 0x00, 0x6b, 0x08, 0x57, 0x55, - 0x9f, 0xfb, 0x16, 0x59, 0xd7, 0x7f, 0x10, 0x00, 0xfb, 0xfd, 0xfe, 0x05, - 0xe2, 0x07, 0x75, 0x89, 0x51, 0xdd, 0x23, 0x07, 0x00, 0xcb, 0xf7, 0xc7, - 0xe3, 0xf1, 0x2b, 0x95, 0xc0, 0x53, 0xe3, 0x90, 0x01, 0xc0, 0x0b, 0xce, - 0x17, 0xaa, 0xae, 0x2c, 0xbf, 0x3d, 0x95, 0x14, 0xe5, 0x7d, 0x12, 0x00, - 0xa8, 0x7a, 0x3e, 0x1c, 0x0e, 0xef, 0x29, 0x85, 0x9d, 0x3b, 0x96, 0x57, - 0x00, 0xa8, 0xfa, 0xc2, 0x54, 0x7d, 0x71, 0x6e, 0x42, 0xd4, 0xcf, 0xf9, - 0x04, 0x70, 0x8b, 0xaa, 0xe7, 0xd4, 0x82, 0x9a, 0x8e, 0xe7, 0x03, 0xc0, - 0x56, 0x55, 0x1d, 0xed, 0xa7, 0x69, 0x32, 0x21, 0x9e, 0x77, 0x0d, 0x80, - 0xf4, 0x3d, 0xde, 0x05, 0x30, 0x67, 0x00, 0xb0, 0xd0, 0x91, 0xbf, 0xc7, - 0x73, 0x01, 0x10, 0xec, 0x3d, 0xbe, 0x0a, 0x40, 0xd3, 0x6b, 0xad, 0x1d, - 0x80, 0xb9, 0x4e, 0xf2, 0x1e, 0x8f, 0x8d, 0xd2, 0x2f, 0xc6, 0xca, 0xec, - 0xd6, 0x54, 0xb0, 0xfd, 0x7c, 0x6b, 0x00, 0x76, 0x87, 0xb1, 0xc5, 0x02, - 0x20, 0xb6, 0x8a, 0xb9, 0xce, 0x57, 0x1c, 0xe0, 0x9a, 0x68, 0x6c, 0xfd, - 0x89, 0x03, 0x62, 0xab, 0x98, 0xeb, 0x7c, 0xc5, 0x01, 0xae, 0x89, 0xc6, - 0xd6, 0x9f, 0x38, 0x20, 0xb6, 0x8a, 0xb9, 0xce, 0x57, 0x1c, 0xe0, 0x9a, - 0x68, 0x6c, 0xfd, 0x89, 0x03, 0x62, 0xab, 0x98, 0x9d, 0x6f, 0xdb, 0xf8, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xd9, 0x1d, 0xa8, 0x02, 0x00, 0x00, 0x00, - 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0xf5, 0xfe, 0x91, 0x81, 0x4a, - 0x65, 0xdb, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, - 0x42, 0x60, 0x82 -}; -unsigned int skip_next_png_len = 651; diff --git a/src/icons/skip_previous.h b/src/icons/skip_previous.h deleted file mode 100644 index 7158272..0000000 --- a/src/icons/skip_previous.h +++ /dev/null @@ -1,61 +0,0 @@ -unsigned char skip_previous_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x02, - 0x64, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x97, 0x2f, 0x6f, 0xc3, - 0x30, 0x10, 0xc5, 0x9b, 0xa8, 0xa0, 0x65, 0x1d, 0x6f, 0xab, 0x06, 0x8e, - 0x0d, 0x96, 0x96, 0x8d, 0x8e, 0x8d, 0x8e, 0x6d, 0x68, 0xfb, 0x26, 0x1b, - 0xdc, 0xd8, 0xd8, 0xe8, 0xd8, 0x68, 0x59, 0xe9, 0xe0, 0x58, 0xd6, 0x3f, - 0xbc, 0x63, 0x2b, 0xa8, 0x94, 0xbd, 0x93, 0x6a, 0x29, 0x8a, 0x34, 0x35, - 0xb5, 0x9d, 0xf3, 0x45, 0xbe, 0x2a, 0x27, 0xc7, 0x6a, 0x1d, 0xdf, 0xfb, - 0xf9, 0xf9, 0xea, 0xa4, 0x9d, 0xc8, 0x3f, 0x0a, 0x20, 0x72, 0x03, 0x74, - 0xd4, 0x01, 0xea, 0x80, 0xc8, 0x09, 0xe8, 0x16, 0x88, 0xdc, 0x00, 0x5a, - 0x04, 0x75, 0x0b, 0xe8, 0x16, 0x88, 0x9c, 0x80, 0x6e, 0x81, 0xc8, 0x0d, - 0xa0, 0xff, 0x02, 0xd1, 0x6d, 0x81, 0xaa, 0xe3, 0x9d, 0x01, 0xac, 0xd7, - 0xeb, 0xa2, 0x1a, 0x79, 0x9e, 0x5f, 0x54, 0x27, 0x6a, 0xa2, 0x8f, 0x79, - 0x17, 0xae, 0xcf, 0x75, 0x06, 0xe0, 0x9a, 0x80, 0xcd, 0x78, 0x00, 0x1e, - 0x40, 0x7c, 0x81, 0xb1, 0x53, 0x84, 0xd3, 0xd5, 0x3a, 0x00, 0x10, 0xfe, - 0xd4, 0xed, 0x76, 0xb7, 0x4e, 0xaa, 0x4b, 0x83, 0x5b, 0x05, 0x00, 0xe2, - 0x7f, 0x91, 0xfb, 0x3d, 0xc2, 0xdb, 0xd5, 0x0a, 0x00, 0xab, 0xd5, 0xea, - 0x1a, 0xe2, 0xc9, 0xf2, 0x3d, 0x6f, 0xca, 0x0f, 0x0f, 0x12, 0x0f, 0x60, - 0xb3, 0xd9, 0xe4, 0x49, 0x92, 0xbc, 0x1d, 0xf2, 0xf5, 0xde, 0x88, 0x05, - 0x80, 0x42, 0x37, 0xa1, 0x55, 0x2f, 0x8a, 0x62, 0xe2, 0x5d, 0x75, 0xe9, - 0x81, 0x22, 0x01, 0x40, 0xf8, 0x07, 0x0a, 0x5d, 0x5e, 0xca, 0xb3, 0xb1, - 0x5b, 0x51, 0x00, 0xb0, 0xea, 0x3d, 0x88, 0x2f, 0xa0, 0xf6, 0x12, 0xc1, - 0x72, 0x89, 0x01, 0xb0, 0x5c, 0x2e, 0x1f, 0xb0, 0xea, 0x54, 0xe5, 0x59, - 0x84, 0x9b, 0x49, 0x44, 0x00, 0xc0, 0xaa, 0x6f, 0xd3, 0x34, 0x7d, 0x34, - 0x49, 0x71, 0xb6, 0x41, 0x01, 0xc0, 0xf2, 0x53, 0x88, 0x27, 0xcb, 0x0f, - 0x38, 0x45, 0x97, 0xe7, 0x0a, 0x06, 0x00, 0xc2, 0x17, 0xb0, 0xbc, 0xf3, - 0x59, 0xbe, 0x2c, 0xc6, 0xe6, 0x9e, 0x1d, 0x00, 0x56, 0xdd, 0xdb, 0x39, - 0xde, 0x46, 0x70, 0x75, 0x0c, 0x2b, 0x00, 0xac, 0xba, 0xd7, 0x73, 0x7c, - 0x55, 0x8c, 0x4d, 0x9f, 0x0d, 0x00, 0xc4, 0x53, 0x85, 0xf7, 0x7a, 0x8e, - 0xaf, 0x23, 0xf8, 0xd8, 0x6f, 0x58, 0x00, 0xc0, 0xf6, 0xde, 0xcf, 0xf0, - 0xc7, 0x84, 0xd5, 0xfd, 0x9e, 0x05, 0x40, 0x96, 0x65, 0xbb, 0xd1, 0x68, - 0xd4, 0xc7, 0xb1, 0xf6, 0xa5, 0x6e, 0x62, 0x5c, 0xbf, 0x63, 0x01, 0x60, - 0xc4, 0x8c, 0xc7, 0xe3, 0xdb, 0xfd, 0x7e, 0x7f, 0x66, 0xfa, 0x12, 0x5a, - 0x56, 0x00, 0x24, 0x18, 0x6e, 0xf8, 0x81, 0x1b, 0x12, 0xdc, 0xcf, 0x11, - 0xc1, 0x2f, 0x76, 0x00, 0x46, 0x31, 0x20, 0xcc, 0xe0, 0x86, 0x99, 0xe9, - 0x87, 0x6a, 0x83, 0x01, 0x20, 0xc1, 0x70, 0xc3, 0x1c, 0x20, 0xf0, 0xba, - 0x9f, 0x7c, 0x53, 0x3f, 0x44, 0x04, 0x05, 0x60, 0x04, 0x0f, 0x87, 0xc3, - 0x0c, 0x05, 0xf2, 0xce, 0xf4, 0x39, 0x5b, 0x11, 0x00, 0x48, 0x30, 0x0a, - 0xe4, 0x33, 0xb6, 0x44, 0x9f, 0xee, 0x39, 0x43, 0x0c, 0x00, 0x12, 0x8d, - 0x2d, 0x41, 0x7f, 0x97, 0xb4, 0x25, 0x5e, 0xa9, 0xcf, 0x11, 0xa2, 0x00, - 0x18, 0xc1, 0xd8, 0x12, 0x37, 0x70, 0xc3, 0xb9, 0xe9, 0x37, 0xd9, 0x8a, - 0x04, 0x40, 0x82, 0xe1, 0x86, 0x2f, 0x8e, 0x02, 0x29, 0x16, 0x00, 0x41, - 0xa0, 0x80, 0x1b, 0xa8, 0x40, 0x5e, 0xd1, 0x7d, 0x13, 0x21, 0x1e, 0x00, - 0x89, 0x46, 0x81, 0x7c, 0xc7, 0x96, 0xa0, 0x02, 0xb9, 0xa3, 0xbe, 0xcf, - 0x68, 0x05, 0x00, 0x12, 0x8c, 0x2d, 0x41, 0x05, 0xd2, 0xfb, 0xfb, 0x44, - 0x6b, 0x00, 0x10, 0x04, 0x0a, 0xb8, 0xc1, 0xeb, 0xfb, 0x44, 0xeb, 0x00, - 0x10, 0x04, 0xb8, 0xe1, 0xdf, 0xf7, 0x09, 0xfa, 0xfe, 0x94, 0x70, 0x06, - 0x40, 0x95, 0xba, 0x1a, 0x48, 0xf0, 0xf3, 0x94, 0x24, 0x6c, 0x7f, 0x8b, - 0x79, 0x9d, 0xdf, 0x25, 0x9c, 0x01, 0xd8, 0x26, 0x2f, 0x65, 0x9c, 0x02, - 0x90, 0xb2, 0x12, 0xa1, 0xf2, 0x50, 0x07, 0x84, 0x22, 0x2f, 0x65, 0x5e, - 0x75, 0x80, 0x94, 0x95, 0x08, 0x95, 0x87, 0x3a, 0x20, 0x14, 0x79, 0x29, - 0xf3, 0xaa, 0x03, 0xa4, 0xac, 0x44, 0xa8, 0x3c, 0xd4, 0x01, 0xa1, 0xc8, - 0x4b, 0x99, 0x57, 0x1d, 0x20, 0x65, 0x25, 0x6c, 0xf3, 0x70, 0x1d, 0xf7, - 0x07, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x69, 0xe5, 0xf3, 0x00, 0x00, 0x00, - 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0x5a, 0x14, 0xa5, 0x81, 0x40, - 0x32, 0x74, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, - 0x42, 0x60, 0x82 -}; -unsigned int skip_previous_png_len = 687; diff --git a/src/icons/zoom_in.h b/src/icons/zoom_in.h deleted file mode 100644 index f5b4647..0000000 --- a/src/icons/zoom_in.h +++ /dev/null @@ -1,121 +0,0 @@ -unsigned char zoom_in_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x05, - 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x9a, 0x3d, 0x54, 0x15, - 0x39, 0x14, 0x80, 0xdf, 0xe3, 0xe7, 0x1c, 0xe8, 0x76, 0x3b, 0xf7, 0xf0, - 0x27, 0x1d, 0x74, 0xd0, 0x69, 0xb7, 0x74, 0x52, 0xad, 0xdb, 0xb9, 0xd5, - 0xae, 0xd5, 0x52, 0xae, 0x1d, 0x56, 0x8b, 0x95, 0x58, 0xa9, 0x95, 0x5a, - 0x69, 0xa7, 0x56, 0xda, 0x89, 0x95, 0x74, 0xda, 0x41, 0x07, 0x1d, 0xf2, - 0x77, 0xd0, 0x4a, 0x3a, 0x5e, 0x01, 0x3c, 0xbf, 0xfb, 0xce, 0x04, 0xc7, - 0x4c, 0x32, 0x49, 0x66, 0x32, 0x1c, 0xe6, 0xf1, 0x38, 0x73, 0xcd, 0xcc, - 0x4d, 0x6e, 0x26, 0xf7, 0xcb, 0xcd, 0xcf, 0xe4, 0xd9, 0xd7, 0xb8, 0xe4, - 0x7f, 0x3d, 0x00, 0x97, 0x3c, 0x00, 0x1a, 0xbd, 0x08, 0xe8, 0x45, 0xc0, - 0x25, 0x27, 0xd0, 0x1b, 0x02, 0x97, 0x3c, 0x00, 0xe2, 0x4f, 0x82, 0x5b, - 0x5b, 0x5b, 0x57, 0xf7, 0xf6, 0xf6, 0x5e, 0xee, 0xee, 0xee, 0xb6, 0x2d, - 0x72, 0xb0, 0xbd, 0xbd, 0xfd, 0xdf, 0x45, 0x01, 0x1f, 0x6d, 0x08, 0x28, - 0xa7, 0x07, 0x06, 0x06, 0xb6, 0xda, 0xed, 0xf6, 0xad, 0x1c, 0x07, 0xaf, - 0xf4, 0xf5, 0xf5, 0x3d, 0x54, 0x70, 0x76, 0x76, 0x76, 0xf2, 0xca, 0xe6, - 0x54, 0x13, 0x27, 0xab, 0x34, 0x00, 0x1c, 0x7f, 0x2e, 0xce, 0x38, 0x9c, - 0xb6, 0xb6, 0xb6, 0xd9, 0x6c, 0xaa, 0x68, 0xf9, 0xdd, 0x5a, 0xa8, 0xc2, - 0x8c, 0xc2, 0x00, 0x08, 0xf5, 0xa1, 0xc4, 0xf1, 0x7f, 0x22, 0xb5, 0xef, - 0x03, 0xf5, 0x6d, 0x44, 0xaa, 0xcb, 0xbb, 0x9a, 0x42, 0x00, 0x70, 0x7e, - 0x86, 0x50, 0x3f, 0xf2, 0x7e, 0x8b, 0x7f, 0xc1, 0x29, 0x20, 0x54, 0x51, - 0xaf, 0xb5, 0x05, 0xc1, 0x00, 0x12, 0xe7, 0xd7, 0xac, 0x35, 0xfe, 0xc8, - 0xd8, 0x64, 0xac, 0x2f, 0x1e, 0x1f, 0x1f, 0xcf, 0x21, 0xb3, 0xa8, 0xe7, - 0x19, 0x26, 0xcf, 0x48, 0x5d, 0x57, 0x27, 0xb2, 0x5c, 0x85, 0x62, 0xe5, - 0x07, 0x01, 0xc0, 0xf9, 0x21, 0x7a, 0x3e, 0xd7, 0x79, 0x9c, 0x9d, 0x1e, - 0x1b, 0x1b, 0x6b, 0x22, 0xd3, 0x23, 0x23, 0x23, 0x0f, 0x26, 0x27, 0x27, - 0x57, 0x91, 0x75, 0x9e, 0x57, 0xc6, 0xc7, 0xc7, 0x17, 0x48, 0x25, 0xaf, - 0x79, 0x7a, 0x7a, 0x7a, 0x2f, 0xcf, 0x89, 0xf3, 0x8a, 0x84, 0x20, 0x00, - 0x38, 0x6f, 0x0d, 0x4f, 0x1c, 0xba, 0x23, 0xce, 0xe1, 0xec, 0x66, 0x9e, - 0x63, 0x2a, 0x6f, 0x62, 0x62, 0x62, 0x49, 0xca, 0x13, 0x15, 0x9f, 0x95, - 0x4e, 0x4b, 0x25, 0x12, 0xde, 0x69, 0xba, 0xd2, 0x8f, 0x7a, 0x05, 0xde, - 0x00, 0x58, 0xae, 0x9e, 0xea, 0xc6, 0xea, 0x99, 0x5e, 0xff, 0x0d, 0x87, - 0x1e, 0xa9, 0xe7, 0x90, 0x94, 0xa8, 0x98, 0x04, 0xde, 0x03, 0x8b, 0xcd, - 0x0d, 0xa2, 0xee, 0x17, 0x4b, 0x5e, 0x14, 0xb5, 0x37, 0x00, 0x96, 0xab, - 0x7f, 0x4d, 0x6f, 0x14, 0xe7, 0xe9, 0xf5, 0x2f, 0xa6, 0x3c, 0x5f, 0x1d, - 0xf0, 0x16, 0xa9, 0xff, 0x95, 0xa9, 0x3c, 0x51, 0x77, 0x60, 0xd2, 0xc7, - 0xd2, 0x79, 0x01, 0x60, 0x3c, 0xbe, 0x31, 0xbd, 0x90, 0x9e, 0xbb, 0xe3, - 0x72, 0x9e, 0x1e, 0x9c, 0x49, 0x64, 0xca, 0x54, 0x87, 0xd2, 0x8d, 0x8e, - 0x8e, 0xfe, 0xc5, 0xfd, 0x21, 0xa2, 0x5f, 0x43, 0xd8, 0x57, 0x16, 0x05, - 0x5e, 0x00, 0x68, 0xd1, 0x4d, 0x24, 0x73, 0xd1, 0x73, 0xce, 0xb0, 0xa7, - 0x07, 0xd7, 0x44, 0x06, 0x07, 0x07, 0x5f, 0x66, 0x2a, 0xd0, 0x14, 0x12, - 0x4d, 0x9a, 0xaa, 0xf3, 0x88, 0x7d, 0x65, 0x73, 0x81, 0x13, 0x00, 0xf4, - 0xaf, 0x74, 0x5a, 0xa1, 0xfd, 0x43, 0x63, 0x67, 0x35, 0x55, 0xe9, 0x47, - 0xa2, 0xa9, 0x65, 0x99, 0x14, 0xaf, 0x95, 0xae, 0xdc, 0x52, 0x81, 0x13, - 0x40, 0x7f, 0x7f, 0xff, 0x92, 0xc9, 0x96, 0xc6, 0xae, 0x9b, 0xf4, 0x65, - 0x75, 0x27, 0x27, 0x27, 0x73, 0x65, 0xeb, 0x08, 0xb1, 0x77, 0x02, 0x60, - 0x72, 0xfa, 0xdb, 0x50, 0x61, 0xa9, 0x49, 0xcf, 0x50, 0xdf, 0x99, 0x0a, - 0xb0, 0xc6, 0x65, 0x91, 0x55, 0xc8, 0x38, 0x0c, 0xcf, 0x0c, 0x0b, 0xde, - 0x38, 0x01, 0x50, 0xef, 0x10, 0xf2, 0xd3, 0xc5, 0xe4, 0xe7, 0xb3, 0xa3, - 0xfb, 0xc9, 0xa6, 0xec, 0x03, 0xbb, 0xca, 0x3f, 0xca, 0xd6, 0x61, 0xb2, - 0xf7, 0x01, 0x90, 0xb1, 0x03, 0xc0, 0xaa, 0xae, 0x64, 0xae, 0x98, 0x61, - 0xb5, 0xc8, 0x9c, 0x01, 0xa8, 0x72, 0x8c, 0xed, 0xdc, 0x7c, 0x55, 0x2e, - 0x49, 0x33, 0x11, 0x86, 0xfd, 0xd5, 0x24, 0x2f, 0x6a, 0x52, 0x08, 0x00, - 0x2d, 0x30, 0x2d, 0x57, 0xa8, 0xa3, 0x5d, 0x99, 0xfa, 0x19, 0x8a, 0x95, - 0x2c, 0x85, 0x85, 0x00, 0xb0, 0x2c, 0x99, 0x56, 0x86, 0x16, 0x8d, 0x5c, - 0xd7, 0x25, 0x85, 0xc4, 0x95, 0x9f, 0x2a, 0xda, 0xc8, 0xf4, 0x36, 0x51, - 0xf7, 0xb5, 0x51, 0xc1, 0x5f, 0x21, 0x00, 0x84, 0x63, 0x66, 0x42, 0x62, - 0xf2, 0xda, 0x64, 0x33, 0x33, 0xab, 0x8b, 0x6a, 0x33, 0x60, 0x72, 0xf3, - 0x55, 0xb9, 0x24, 0xcd, 0xcc, 0x3b, 0xac, 0x46, 0xb9, 0x1f, 0x61, 0x89, - 0x5d, 0x70, 0xe2, 0x03, 0x20, 0x33, 0x1e, 0x71, 0xc6, 0xb4, 0x32, 0x04, - 0xbf, 0x3c, 0xc4, 0x80, 0x08, 0x78, 0x1f, 0x52, 0xde, 0xb7, 0xac, 0x0f, - 0x00, 0xd3, 0x6e, 0x2f, 0xd3, 0x43, 0xbe, 0x2f, 0x74, 0x95, 0x63, 0xb9, - 0x33, 0x9e, 0x11, 0xf2, 0xe5, 0x98, 0x99, 0x78, 0x5d, 0x75, 0xf9, 0xe4, - 0x3b, 0x01, 0xb0, 0xe3, 0x7b, 0x6c, 0xaa, 0x68, 0x7f, 0x7f, 0xff, 0xbe, - 0x49, 0x5f, 0x56, 0x47, 0x74, 0x39, 0xb7, 0xcc, 0x65, 0xdf, 0x91, 0xb6, - 0x77, 0x02, 0x60, 0x6c, 0xb7, 0xd2, 0x06, 0xea, 0x9e, 0x90, 0x5c, 0x54, - 0xf7, 0xb1, 0x52, 0x96, 0x51, 0xe3, 0xc1, 0x28, 0x73, 0x4e, 0xe1, 0x7d, - 0x87, 0xab, 0x6d, 0x4e, 0x00, 0x52, 0x01, 0x0d, 0xb8, 0x2d, 0xa9, 0x2e, - 0x34, 0xd8, 0x79, 0x88, 0x49, 0xe8, 0x76, 0x4e, 0x80, 0x64, 0x72, 0xd4, - 0xed, 0x0d, 0xcf, 0x1f, 0x0c, 0xba, 0x06, 0x67, 0x06, 0x0b, 0x26, 0x7d, - 0x0c, 0x9d, 0x17, 0x00, 0x1a, 0xf0, 0xc2, 0xf2, 0xb2, 0x29, 0x8e, 0xc5, - 0x9f, 0x58, 0xf2, 0x82, 0xd4, 0xc0, 0x6c, 0x5b, 0x0c, 0xde, 0x5a, 0xf4, - 0x51, 0xd4, 0x5e, 0x00, 0xe4, 0x4d, 0xcc, 0x05, 0xd7, 0x25, 0xd5, 0x85, - 0xe8, 0x58, 0xa0, 0xf1, 0xc6, 0xf3, 0x02, 0xbd, 0xac, 0xed, 0x19, 0x7b, - 0x9b, 0xf3, 0x0d, 0x22, 0xe8, 0x4f, 0x9b, 0x5d, 0x0c, 0xbd, 0x37, 0x00, - 0xe6, 0x82, 0x4f, 0xbc, 0xd0, 0x76, 0xde, 0x77, 0x13, 0x27, 0x8e, 0xd8, - 0x0e, 0x07, 0xad, 0x0e, 0x94, 0xbf, 0x86, 0x9d, 0xd5, 0x79, 0x1b, 0x74, - 0xda, 0x11, 0xed, 0xf2, 0x06, 0x20, 0x6f, 0xa4, 0x37, 0xa6, 0x25, 0xb5, - 0x88, 0x9c, 0x18, 0x1f, 0xe1, 0xd0, 0x06, 0x8e, 0x99, 0x76, 0x8a, 0x67, - 0x66, 0xb2, 0xd4, 0x51, 0xae, 0xcd, 0x8e, 0xf2, 0xe3, 0x99, 0x32, 0x7b, - 0xb3, 0x92, 0x40, 0xcf, 0xe6, 0x44, 0xd4, 0x04, 0x01, 0x90, 0xf7, 0x02, - 0xa1, 0x29, 0x69, 0x8e, 0x4c, 0xe1, 0xd8, 0x81, 0x38, 0x98, 0xc8, 0x37, - 0xd2, 0x0d, 0x44, 0xe0, 0x74, 0x3e, 0x96, 0x3c, 0x97, 0xba, 0x1b, 0xcc, - 0x2f, 0x6b, 0x22, 0xfc, 0x98, 0x6a, 0x3c, 0x93, 0xc8, 0x69, 0x83, 0x77, - 0x56, 0x30, 0x00, 0xa9, 0x39, 0x81, 0x60, 0x5c, 0x1e, 0x25, 0x5f, 0x13, - 0xf9, 0x88, 0x91, 0xf3, 0x40, 0xdf, 0xe1, 0xb1, 0xa2, 0xec, 0x99, 0x5f, - 0x66, 0x44, 0xf8, 0x14, 0xfe, 0xbf, 0x2a, 0x08, 0x85, 0x00, 0x48, 0x03, - 0x81, 0x30, 0x4c, 0x1a, 0x75, 0x86, 0x26, 0x32, 0xae, 0x53, 0xef, 0x3c, - 0x69, 0xe6, 0xb4, 0xa9, 0x2a, 0x08, 0x85, 0x01, 0xe0, 0x7c, 0x67, 0x86, - 0x66, 0xa2, 0x12, 0x10, 0xbe, 0xd1, 0x20, 0x66, 0x26, 0x59, 0xc1, 0xf1, - 0x26, 0x7b, 0x05, 0x99, 0x68, 0x4d, 0xf9, 0x1d, 0x5d, 0x15, 0x10, 0x4a, - 0x01, 0x90, 0x56, 0x31, 0x51, 0xb5, 0x68, 0xfc, 0x30, 0x20, 0x7e, 0xe5, - 0x39, 0xd7, 0x01, 0xf2, 0xf5, 0xeb, 0x31, 0xb6, 0xb2, 0x51, 0x9a, 0x4f, - 0x67, 0x70, 0x2e, 0xf8, 0x3a, 0xfd, 0x9c, 0xbe, 0x8f, 0x0d, 0xa1, 0x34, - 0x00, 0xd5, 0x38, 0x40, 0x1c, 0xe2, 0x8c, 0x84, 0x70, 0x13, 0x18, 0xc3, - 0x34, 0xf4, 0x16, 0xa1, 0x2c, 0x1b, 0xa8, 0x55, 0x52, 0x09, 0xe9, 0x55, - 0x74, 0xcb, 0xe4, 0xcd, 0x51, 0x4e, 0x9c, 0x16, 0x31, 0xfe, 0x4f, 0x11, - 0x8e, 0xdb, 0x97, 0xd9, 0x6a, 0x5b, 0x7f, 0x3b, 0xa4, 0x9e, 0x68, 0x73, - 0x42, 0x34, 0x00, 0x0a, 0x84, 0xa4, 0xc0, 0x68, 0xf1, 0xc3, 0xe8, 0x6b, - 0x42, 0xfa, 0x36, 0xce, 0xce, 0x91, 0xce, 0x4a, 0x8a, 0xee, 0x2e, 0x79, - 0x5e, 0x5f, 0x75, 0x40, 0x58, 0x3a, 0x0f, 0x08, 0x95, 0x00, 0x10, 0x08, - 0x31, 0xe4, 0x3c, 0x20, 0x5c, 0x68, 0x00, 0x02, 0xb1, 0x6a, 0x08, 0x17, - 0x1e, 0x40, 0xd5, 0x10, 0x6a, 0x01, 0x20, 0x04, 0x82, 0x94, 0x0d, 0x91, - 0xda, 0x00, 0x10, 0xa7, 0xaa, 0x18, 0x0e, 0xb5, 0x02, 0x50, 0x05, 0x84, - 0xda, 0x01, 0x08, 0x80, 0xe0, 0x75, 0x64, 0x57, 0x4b, 0x00, 0x1e, 0x10, - 0x0e, 0x19, 0x2e, 0xcb, 0x52, 0xce, 0x25, 0xb5, 0x05, 0x20, 0x8e, 0xe1, - 0xa4, 0x69, 0xb3, 0x24, 0x3b, 0x52, 0xd9, 0x96, 0x4b, 0x11, 0xa7, 0xd4, - 0x1a, 0x80, 0x78, 0xa7, 0x41, 0x08, 0x72, 0x5e, 0xec, 0x6b, 0x0f, 0x40, - 0x9c, 0x48, 0x20, 0xdc, 0x65, 0xbb, 0xed, 0xdd, 0xf3, 0x62, 0x27, 0xd2, - 0x15, 0x00, 0xc4, 0x11, 0x20, 0x78, 0x8d, 0x79, 0x29, 0x9b, 0x96, 0xae, - 0x01, 0x90, 0x76, 0x2a, 0xe4, 0xbe, 0x07, 0x20, 0x84, 0x56, 0x37, 0x96, - 0xed, 0x45, 0x40, 0x37, 0xf6, 0x6a, 0x88, 0x4f, 0xbd, 0x08, 0x08, 0xa1, - 0xd5, 0x8d, 0x65, 0x6b, 0x1f, 0x01, 0x65, 0x3b, 0xe5, 0x3b, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x51, 0xf8, 0x77, 0x07, 0x00, 0x00, 0x00, 0x06, 0x49, - 0x44, 0x41, 0x54, 0x03, 0x00, 0x1b, 0xcd, 0x54, 0x9f, 0x16, 0x6a, 0xa8, - 0x66, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, - 0x82 -}; -unsigned int zoom_in_png_len = 1405; diff --git a/src/icons/zoom_out.h b/src/icons/zoom_out.h deleted file mode 100644 index 2c6f933..0000000 --- a/src/icons/zoom_out.h +++ /dev/null @@ -1,117 +0,0 @@ -unsigned char zoom_out_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0x69, 0x71, 0xde, 0x00, 0x00, 0x05, - 0x06, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0xec, 0x9a, 0x3d, 0x54, 0xd4, - 0x40, 0x10, 0x80, 0x39, 0xa0, 0x80, 0x4e, 0x3b, 0x7c, 0xfc, 0x77, 0xd0, - 0x41, 0x07, 0x9d, 0x74, 0x50, 0x89, 0x1d, 0x56, 0x4a, 0x25, 0xa5, 0x74, - 0x50, 0x89, 0x95, 0x50, 0x29, 0x95, 0x5a, 0x69, 0x87, 0x54, 0xda, 0x89, - 0x15, 0xd7, 0x69, 0x07, 0x1d, 0x74, 0x27, 0x7f, 0x0f, 0xad, 0xa4, 0xe3, - 0x0a, 0xe0, 0xfc, 0xe6, 0x5e, 0x72, 0xde, 0x25, 0xbb, 0xc9, 0x6e, 0xb2, - 0xe1, 0x91, 0xbb, 0xf3, 0x65, 0xdc, 0x64, 0x76, 0x77, 0xb2, 0xf3, 0xed, - 0xec, 0x4f, 0xf6, 0xe8, 0xec, 0x68, 0xf1, 0x7f, 0x6d, 0x00, 0x2d, 0x1e, - 0x00, 0x1d, 0xed, 0x08, 0x68, 0x47, 0x40, 0x8b, 0x13, 0x68, 0x0f, 0x81, - 0x16, 0x0f, 0x00, 0xf7, 0x93, 0x60, 0xa9, 0x54, 0x1a, 0x39, 0x3d, 0x3d, - 0xdd, 0x3a, 0x39, 0x39, 0xa9, 0x68, 0xe4, 0xfc, 0xe8, 0xe8, 0xe8, 0xc5, - 0x5d, 0x01, 0xef, 0x6c, 0x08, 0xf8, 0x4e, 0x77, 0x77, 0x77, 0x97, 0x2a, - 0x95, 0xca, 0x42, 0x84, 0x83, 0x7d, 0x9d, 0x9d, 0x9d, 0x6f, 0x7c, 0x38, - 0xc7, 0xc7, 0xc7, 0x51, 0x65, 0x23, 0xcc, 0xb8, 0xc9, 0x4a, 0x0d, 0x00, - 0xc7, 0x3f, 0x8a, 0x33, 0x31, 0x4e, 0x6b, 0x5b, 0x5b, 0x28, 0x14, 0xfc, - 0x68, 0x79, 0xa8, 0x2d, 0x94, 0x61, 0x46, 0x62, 0x00, 0x84, 0x7a, 0x8f, - 0xe7, 0xf8, 0x33, 0x47, 0xed, 0xdb, 0xc5, 0xde, 0x81, 0x23, 0x5b, 0xc6, - 0x66, 0x12, 0x01, 0xc0, 0xf9, 0x09, 0x42, 0xfd, 0xd2, 0xf8, 0x2d, 0xe6, - 0x05, 0xc7, 0x80, 0x90, 0x85, 0x5d, 0x6d, 0x0b, 0xac, 0x01, 0x78, 0xce, - 0xef, 0x69, 0x2d, 0xfe, 0xcf, 0x38, 0x64, 0xac, 0xaf, 0x5c, 0x5d, 0x5d, - 0xcd, 0x20, 0x93, 0xa8, 0xe7, 0x18, 0x26, 0x1f, 0x48, 0xe3, 0xae, 0x6a, - 0x64, 0xc5, 0x15, 0x72, 0x95, 0x6f, 0x05, 0x00, 0xe7, 0x7b, 0xe8, 0xf9, - 0x48, 0xe7, 0x71, 0x76, 0x7c, 0x70, 0x70, 0xb0, 0x80, 0x8c, 0xf7, 0xf7, - 0xf7, 0x6f, 0x8c, 0x8e, 0x8e, 0x16, 0x91, 0x7d, 0x9e, 0x77, 0x86, 0x86, - 0x86, 0x96, 0x48, 0x25, 0xaf, 0x70, 0x73, 0x73, 0xf3, 0x2a, 0xca, 0x89, - 0xdb, 0x8a, 0x04, 0x2b, 0x00, 0x38, 0xaf, 0x0d, 0x4f, 0x1c, 0x5a, 0x16, - 0xe7, 0x70, 0xf6, 0x30, 0xca, 0x31, 0x3f, 0x6f, 0x78, 0x78, 0x78, 0x4d, - 0xca, 0x13, 0x15, 0xbf, 0x7c, 0x5d, 0x20, 0x95, 0x48, 0xf8, 0x16, 0xd0, - 0xa5, 0x7e, 0x0c, 0x1a, 0x30, 0x06, 0xc0, 0x72, 0xf5, 0x3e, 0x58, 0xd9, - 0x7f, 0xa6, 0xd7, 0x1f, 0xe0, 0xd0, 0x5b, 0xff, 0xd9, 0x26, 0x25, 0x2a, - 0x46, 0x81, 0xb7, 0xa1, 0xa9, 0x33, 0x4b, 0xd4, 0xdd, 0xd3, 0xe4, 0x39, - 0x51, 0x1b, 0x03, 0x60, 0xb9, 0x7a, 0xae, 0x7a, 0xa3, 0x38, 0x4f, 0xaf, - 0xff, 0x56, 0xe5, 0x99, 0xea, 0x80, 0xb7, 0x82, 0xfd, 0xcf, 0xaa, 0xf2, - 0x44, 0xdd, 0xb9, 0x4a, 0xef, 0x4a, 0x67, 0x04, 0x80, 0xf1, 0xf8, 0x45, - 0xf5, 0x42, 0x7a, 0x6e, 0x39, 0xad, 0xf3, 0xbe, 0xdd, 0x81, 0x81, 0x81, - 0x27, 0xdc, 0x5f, 0x20, 0xc1, 0xab, 0x27, 0xcb, 0x28, 0x30, 0x02, 0x40, - 0x8b, 0xe6, 0x91, 0xd0, 0x45, 0xcf, 0x25, 0x0a, 0xfb, 0x90, 0x21, 0x4f, - 0x21, 0xd1, 0xe4, 0xdd, 0x36, 0x24, 0x44, 0x41, 0x66, 0x73, 0x41, 0x2c, - 0x00, 0xe8, 0xf7, 0x35, 0xb4, 0xc6, 0x7b, 0xa0, 0xb1, 0x93, 0xde, 0xad, - 0xb3, 0x84, 0x68, 0x2a, 0x6b, 0x26, 0xc5, 0x29, 0x67, 0x2f, 0x09, 0x18, - 0x8a, 0x05, 0xd0, 0xd5, 0xd5, 0xb5, 0x16, 0xa8, 0x53, 0x7d, 0xa4, 0xb1, - 0xfb, 0xd5, 0x1b, 0xc7, 0xff, 0x5d, 0x5f, 0x5f, 0xcf, 0x38, 0x36, 0x19, - 0x69, 0x2e, 0x16, 0x00, 0x93, 0xd3, 0x53, 0x85, 0x85, 0x54, 0x93, 0x9e, - 0xc2, 0x5e, 0x4d, 0x05, 0x58, 0xe5, 0xb2, 0xc8, 0x2a, 0xa4, 0x1c, 0x86, - 0xb5, 0x8a, 0x09, 0x6f, 0x62, 0x01, 0x60, 0xb7, 0x07, 0x69, 0xb8, 0x98, - 0xfc, 0x4c, 0x76, 0x74, 0x0d, 0x75, 0xd2, 0x3e, 0xb0, 0xab, 0x7c, 0x94, - 0xd6, 0x86, 0xaa, 0xbe, 0x09, 0x80, 0x50, 0x3d, 0x00, 0x14, 0x83, 0x4a, - 0xe6, 0x8a, 0x09, 0x56, 0x0b, 0xdd, 0x19, 0x80, 0x56, 0x1f, 0xb4, 0xe3, - 0x3d, 0x87, 0x22, 0x8c, 0xb9, 0x61, 0xc4, 0xcb, 0x73, 0x9a, 0x24, 0x02, - 0x40, 0x0b, 0x54, 0xcb, 0x15, 0x6a, 0x67, 0x57, 0xc8, 0x3e, 0x43, 0x31, - 0x93, 0x0d, 0x51, 0x22, 0x00, 0x2c, 0x4b, 0xaa, 0x95, 0xa1, 0x4c, 0x23, - 0xf7, 0x6d, 0x45, 0x83, 0x2c, 0xd4, 0xdb, 0x44, 0xdd, 0x1f, 0x4d, 0xd9, - 0x54, 0xea, 0x44, 0x00, 0x08, 0xc7, 0xd0, 0x84, 0xc4, 0xe4, 0x75, 0xc8, - 0x66, 0x66, 0xd2, 0x56, 0x34, 0xad, 0x0f, 0xcd, 0x3b, 0xac, 0x46, 0x91, - 0x1f, 0x61, 0x1a, 0x3b, 0xb1, 0x6a, 0x13, 0x00, 0xa1, 0xf1, 0x48, 0x2f, - 0xab, 0x56, 0x86, 0xd8, 0x97, 0xa5, 0x29, 0x40, 0x04, 0x7c, 0x4f, 0x53, - 0x5f, 0x57, 0xd7, 0x04, 0x80, 0x6a, 0xb7, 0x17, 0xea, 0x21, 0xdd, 0x0b, - 0x6c, 0xf5, 0x2c, 0x77, 0xca, 0x33, 0x42, 0xbe, 0x1c, 0x43, 0x13, 0xaf, - 0xad, 0x6d, 0x55, 0xf9, 0x58, 0x00, 0xec, 0xf8, 0x36, 0x55, 0x15, 0xcf, - 0xce, 0xce, 0x5e, 0xab, 0xf4, 0x69, 0x75, 0x44, 0xd7, 0x56, 0x5a, 0x1b, - 0x36, 0xf5, 0x63, 0x01, 0x30, 0xb6, 0xcb, 0x2a, 0x83, 0x84, 0xe4, 0x8a, - 0x4a, 0x9f, 0x46, 0xc7, 0x32, 0xaa, 0x3c, 0x18, 0x65, 0xce, 0x49, 0xbc, - 0xef, 0x88, 0x6b, 0x4f, 0x2c, 0x00, 0x31, 0x40, 0x03, 0x16, 0x25, 0x0d, - 0x0a, 0x0d, 0x76, 0x7d, 0x88, 0xb9, 0x1b, 0x7c, 0x87, 0x3c, 0x73, 0x66, - 0xb0, 0x24, 0x69, 0x16, 0x62, 0x04, 0x80, 0x06, 0x7c, 0xd2, 0xbc, 0x7c, - 0x8c, 0x63, 0xf1, 0x77, 0x9a, 0x3c, 0x2b, 0x35, 0x30, 0x2b, 0x9a, 0x0a, - 0x5f, 0x35, 0x7a, 0x27, 0x6a, 0x23, 0x00, 0xf2, 0x26, 0xe6, 0x82, 0x69, - 0x49, 0x83, 0x42, 0x74, 0x2c, 0xd1, 0x78, 0xe5, 0x79, 0x41, 0xb0, 0xac, - 0xee, 0x99, 0xfa, 0x3a, 0xe7, 0x3b, 0x98, 0xfc, 0x1e, 0xeb, 0xea, 0xb9, - 0xd0, 0x1b, 0x03, 0x60, 0x2e, 0xf8, 0xc9, 0x0b, 0x75, 0xe7, 0x7d, 0xf3, - 0x38, 0x71, 0xc9, 0x76, 0xd8, 0x6a, 0x75, 0xa0, 0xfc, 0x14, 0xf5, 0xb4, - 0xce, 0xeb, 0xa0, 0xd3, 0x0e, 0x67, 0x97, 0x31, 0x00, 0x79, 0x23, 0xbd, - 0x31, 0x2e, 0xa9, 0x46, 0xe4, 0xc4, 0xf8, 0x12, 0x87, 0x0e, 0x70, 0x4c, - 0xb5, 0x53, 0xac, 0x55, 0x93, 0xa5, 0x8e, 0x72, 0x15, 0x76, 0x94, 0x3f, - 0x6a, 0xca, 0xf0, 0xcd, 0x8e, 0x07, 0x3d, 0x9c, 0xe3, 0x50, 0x63, 0x05, - 0x40, 0xde, 0x0b, 0x84, 0x82, 0xa4, 0x11, 0x32, 0x86, 0x63, 0xe7, 0xe2, - 0xa0, 0x27, 0x7f, 0x49, 0x0f, 0x10, 0x81, 0x53, 0xfd, 0x28, 0x32, 0x5c, - 0xea, 0x66, 0x99, 0x5f, 0xf6, 0x44, 0xf8, 0x31, 0x55, 0x79, 0x26, 0x11, - 0xd1, 0x06, 0xe3, 0x2c, 0x6b, 0x00, 0x62, 0xd9, 0x83, 0xa0, 0x5c, 0x1e, - 0x25, 0x3f, 0x20, 0xf2, 0x11, 0x33, 0x86, 0xce, 0x74, 0x78, 0xec, 0x50, - 0xb6, 0x7a, 0x31, 0xbf, 0x4c, 0x88, 0xf0, 0x29, 0xfc, 0x32, 0x2b, 0x08, - 0x89, 0x00, 0x48, 0xeb, 0x80, 0xd0, 0x4b, 0xea, 0x74, 0x86, 0x26, 0x32, - 0xa6, 0xb1, 0x3b, 0x47, 0x1a, 0x3a, 0x6d, 0xca, 0x0a, 0x42, 0x62, 0x00, - 0x38, 0x5f, 0x9d, 0xa1, 0x99, 0xa8, 0x04, 0x84, 0x69, 0x34, 0x48, 0x35, - 0x95, 0xec, 0xe0, 0x78, 0x81, 0x0f, 0x29, 0x99, 0x68, 0x55, 0xf9, 0x55, - 0x5d, 0x16, 0x10, 0x52, 0x01, 0x90, 0x56, 0x31, 0x51, 0x95, 0x69, 0x7c, - 0x2f, 0x20, 0xee, 0xf3, 0x1c, 0xe9, 0x00, 0xf9, 0xc1, 0x6b, 0x93, 0xba, - 0xf2, 0x53, 0xd9, 0x5c, 0x7d, 0x06, 0xe7, 0x82, 0xdb, 0xf5, 0xcf, 0xf5, - 0xf7, 0xae, 0x21, 0xa4, 0x06, 0xe0, 0x37, 0x0e, 0x10, 0x17, 0x38, 0x23, - 0x21, 0x5c, 0x00, 0x46, 0x2f, 0x0d, 0x5d, 0x20, 0x94, 0x65, 0x03, 0x55, - 0x24, 0x95, 0x90, 0x2e, 0xa2, 0x5b, 0x27, 0x6f, 0x86, 0x72, 0xe2, 0xb4, - 0x88, 0xf2, 0x2f, 0x45, 0x38, 0x6e, 0x5f, 0x67, 0xab, 0xad, 0xfd, 0xed, - 0x10, 0x3b, 0xce, 0xe6, 0x04, 0x67, 0x00, 0x7c, 0x10, 0x92, 0x02, 0xa3, - 0xcc, 0x0f, 0xa3, 0xdb, 0x84, 0xf4, 0x22, 0xce, 0xce, 0x90, 0x4e, 0x4a, - 0x8a, 0x6e, 0x95, 0x3c, 0xa3, 0xaf, 0x3a, 0x20, 0xac, 0xdd, 0x06, 0x84, - 0x4c, 0x00, 0x08, 0x04, 0x17, 0x72, 0x1b, 0x10, 0xee, 0x34, 0x00, 0x81, - 0x98, 0x35, 0x84, 0x3b, 0x0f, 0x20, 0x6b, 0x08, 0xb9, 0x00, 0x60, 0x03, - 0x41, 0xca, 0xda, 0x48, 0x6e, 0x00, 0x88, 0x53, 0x59, 0x0c, 0x87, 0x5c, - 0x01, 0xc8, 0x02, 0x42, 0xee, 0x00, 0x58, 0x40, 0x30, 0x3a, 0xb2, 0xcb, - 0x25, 0x00, 0x03, 0x08, 0x17, 0x0c, 0x97, 0x75, 0x29, 0x17, 0x27, 0xb9, - 0x05, 0x20, 0x8e, 0xe1, 0xa4, 0x6a, 0xb3, 0x24, 0x3b, 0x52, 0xd9, 0x96, - 0x4b, 0x91, 0x58, 0xc9, 0x35, 0x00, 0xf1, 0x2e, 0x00, 0xc1, 0xca, 0x79, - 0xa9, 0x9f, 0x7b, 0x00, 0xe2, 0x84, 0x07, 0x61, 0x95, 0xed, 0xb6, 0x71, - 0xcf, 0x4b, 0x3d, 0x91, 0xa6, 0x00, 0x20, 0x8e, 0x00, 0xc1, 0x68, 0xcc, - 0x4b, 0xd9, 0x7a, 0x69, 0x1a, 0x00, 0xf5, 0x4e, 0xd9, 0xdc, 0xb7, 0x01, - 0xd8, 0xd0, 0x6a, 0xc6, 0xb2, 0xed, 0x08, 0x68, 0xc6, 0x5e, 0xb5, 0xf1, - 0xa9, 0x1d, 0x01, 0x36, 0xb4, 0x9a, 0xb1, 0x6c, 0xee, 0x23, 0x20, 0x6d, - 0xa7, 0xfc, 0x03, 0x00, 0x00, 0xff, 0xff, 0x23, 0x51, 0x17, 0x5c, 0x00, - 0x00, 0x00, 0x06, 0x49, 0x44, 0x41, 0x54, 0x03, 0x00, 0x9b, 0xeb, 0x52, - 0x9f, 0x91, 0xa9, 0x83, 0x89, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, - 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int zoom_out_png_len = 1361; diff --git a/src/import_dialog.cpp b/src/import_dialog.cpp new file mode 100644 index 0000000..6b3af81 --- /dev/null +++ b/src/import_dialog.cpp @@ -0,0 +1,302 @@ +#include "import_dialog.h" +#include "sprite_converter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ImportDialog::ImportDialog(QWidget* parent, AppState& state) : QDialog(parent), m_state(state) +{ + setWindowTitle("Create SPR"); + setFixedSize(450, 580); + + auto* lay = new QVBoxLayout(this); + lay->setContentsMargins(24, 24, 24, 24); + lay->setSpacing(0); + + auto* inLbl = new QLabel("Input Images"); + inLbl->setStyleSheet(QString("font-size: 14px; font-weight: 500; color: %1;").arg(SpriteColors::TextSection.name())); + lay->addWidget(inLbl); + lay->addSpacing(8); + + auto* btnRow = new QHBoxLayout; + btnRow->setSpacing(8); + auto* addBtn = new QPushButton("Add Images..."); + addBtn->setCursor(Qt::PointingHandCursor); + addBtn->setStyleSheet(QString( + "QPushButton { background: %1; color: %2; border: none; border-radius: 4px; padding: 6px 12px; }" + "QPushButton:hover { background: %3; }" + ).arg(SpriteColors::BgLighter.name(), SpriteColors::TextPrimary.name(), SpriteColors::BgLight.name())); + + auto* clrBtn = new QPushButton("Clear"); + clrBtn->setCursor(Qt::PointingHandCursor); + clrBtn->setStyleSheet(addBtn->styleSheet()); + + btnRow->addWidget(addBtn); + btnRow->addWidget(clrBtn); + btnRow->addStretch(); + lay->addLayout(btnRow); + lay->addSpacing(8); + + m_fileList = new QListWidget; + m_fileList->setFixedHeight(130); + m_fileList->setStyleSheet(QString( + "QListWidget { background: %1; border: 1px solid %2; border-radius: 4px; }" + ).arg(SpriteColors::BgDark.name(), SpriteColors::Border.name())); + lay->addWidget(m_fileList); + lay->addSpacing(24); + + auto* setLbl = new QLabel("Sprite Parameters"); + setLbl->setStyleSheet(inLbl->styleSheet()); + lay->addWidget(setLbl); + lay->addSpacing(12); + + auto addRow = [&](const QString& label, QWidget* w) { + auto* r = new QHBoxLayout; + auto* l = new QLabel(label); + l->setFixedWidth(80); + l->setStyleSheet(QString("color: %1;").arg(SpriteColors::TextDim.name())); + r->addWidget(l); + r->addWidget(w); + lay->addLayout(r); + lay->addSpacing(8); + }; + + auto* nameEd = new QLineEdit("output"); + nameEd->setStyleSheet(QString( + "QLineEdit { background: %1; color: %2; border: 1px solid %3; border-radius: 4px; padding: 4px; }" + "QLineEdit:focus { border: 1px solid %4; }" + ).arg(SpriteColors::BgLight.name(), SpriteColors::TextPrimary.name(), + SpriteColors::Border.name(), SpriteColors::Accent.name())); + addRow("Name:", nameEd); + + auto* verW = new QWidget; + auto* verL = new QHBoxLayout(verW); + verL->setContentsMargins(0,0,0,0); + auto* v1 = new QRadioButton("Quake (v1)"); + auto* v2 = new QRadioButton("Half-Life (v2)"); + v2->setChecked(true); + verL->addWidget(v1); + verL->addWidget(v2); + verL->addStretch(); + + m_versionCombo = new QComboBox; + m_versionCombo->addItem("", 1); m_versionCombo->addItem("", 2); + m_versionCombo->setVisible(false); + + connect(v1, &QRadioButton::toggled, [this](bool c) { if(c) m_versionCombo->setCurrentIndex(0); }); + connect(v2, &QRadioButton::toggled, [this](bool c) { if(c) m_versionCombo->setCurrentIndex(1); }); + + addRow("Version:", verW); + + m_typeCombo = new QComboBox; + m_typeCombo->addItems({"Parallel Upright", "Facing Upright", "Parallel", "Oriented", "Parallel Oriented"}); + m_typeCombo->setCurrentIndex(2); + addRow("Type:", m_typeCombo); + + m_renderCombo = new QComboBox; + m_renderCombo->addItems({"Normal", "Additive", "Index Alpha", "Alpha Test"}); + addRow("Render:", m_renderCombo); + + auto* intW = new QWidget; + auto* intL = new QHBoxLayout(intW); + intL->setContentsMargins(0,0,0,0); + + m_intervalSpin = new QDoubleSpinBox; + m_intervalSpin->setRange(0.01, 1.0); m_intervalSpin->setValue(0.1); + m_intervalSpin->setVisible(false); + + auto* intSl = new QSlider(Qt::Horizontal); + intSl->setRange(1, 100); intSl->setValue(10); + intSl->setFixedHeight(40); + auto* intLbl = new QLabel("0.10s"); + intLbl->setFixedWidth(40); + intLbl->setStyleSheet(QString("color: %1;").arg(SpriteColors::TextPrimary.name())); + + connect(intSl, &QSlider::valueChanged, [=](int v) { + float val = v / 100.0f; + m_intervalSpin->setValue(val); + intLbl->setText(QString::number(val, 'f', 2) + "s"); + }); + + intL->addWidget(intSl); + intL->addWidget(intLbl); + addRow("Interval:", intW); + + connect(m_versionCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int) { m_renderCombo->setEnabled(m_versionCombo->currentData().toInt() == 2); }); + + lay->addStretch(); + + auto* footRow = new QHBoxLayout; + footRow->setSpacing(8); + footRow->addStretch(); + + auto* cancelBtn = new QPushButton("Cancel"); + cancelBtn->setCursor(Qt::PointingHandCursor); + cancelBtn->setStyleSheet(QString( + "QPushButton { background: transparent; color: %1; border: none; font-weight: 500; padding: 0 12px; height: 36px; }" + "QPushButton:hover { background: rgba(255, 255, 255, 0.05); border-radius: 4px; }" + ).arg(SpriteColors::Accent.name())); + + auto* createBtn = new QPushButton("Create"); + createBtn->setCursor(Qt::PointingHandCursor); + createBtn->setStyleSheet(QString( + "QPushButton { background: %1; color: #FFFFFF; border: none; border-radius: 4px; font-weight: 500; padding: 0 16px; height: 36px; }" + "QPushButton:hover { background: %2; }" + ).arg(SpriteColors::Accent.name(), SpriteColors::AccentLit.name())); + + footRow->addWidget(cancelBtn); + footRow->addWidget(createBtn); + lay->addLayout(footRow); + + connect(addBtn, &QPushButton::clicked, this, &ImportDialog::onAddImages); + connect(clrBtn, &QPushButton::clicked, this, &ImportDialog::onClearAll); + connect(createBtn, &QPushButton::clicked, [=]() { + m_outputFile = nameEd->text(); + this->onCreate(); + }); + connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); +} + +void ImportDialog::onAddImages() +{ + QSettings settings("Sprite-Tools"); + QString lastDir = settings.value("lastImportDir", QString::fromStdString(m_state.last_dir)).toString(); + + QStringList files = QFileDialog::getOpenFileNames(this, "Select Images", lastDir, "Images (*.png *.bmp);;All (*)"); + + if (files.isEmpty()) + return; + + QString newDir = QFileInfo(files.first()).absolutePath(); + + m_state.last_dir = newDir.toStdString(); + + settings.setValue("lastImportDir", newDir); + + for (const auto& f : files) + { + m_files.append(f); + + auto* item = new QListWidgetItem(m_fileList); + auto* w = new FileListItem(QFileInfo(f).fileName(), [this, item, f]() { + int row = m_fileList->row(item); + m_files.removeAt(row); + delete m_fileList->takeItem(row); + }); + item->setSizeHint(w->sizeHint()); + m_fileList->setItemWidget(item, w); + } +} + +void ImportDialog::onClearAll() +{ + m_files.clear(); + m_fileList->clear(); +} + +void ImportDialog::onRemoveSelected() +{ + auto items = m_fileList->selectedItems(); + for (auto* item : items) + { + int row = m_fileList->row(item); + m_files.removeAt(row); + delete m_fileList->takeItem(row); + } +} + +void ImportDialog::onCreate() +{ + if (m_files.isEmpty()) + { + QMessageBox::warning(this, "No Images", "Please add at least one image."); + return; + } + + QString savePath = QFileDialog::getSaveFileName(this, "Save Sprite", QString::fromStdString(m_state.last_dir), "Sprite (*.spr)"); + if (savePath.isEmpty()) return; + if (!savePath.endsWith(".spr", Qt::CaseInsensitive)) savePath += ".spr"; + + int version = m_versionCombo->currentData().toInt(); + int type = m_typeCombo->currentIndex(); + int texFmt = m_renderCombo->currentIndex(); + float interval = (float)m_intervalSpin->value(); + + QProgressDialog prog("Creating sprite...", "Cancel", 0, 100, this); + prog.setWindowTitle("Creating Sprite"); + prog.setWindowModality(Qt::WindowModal); + prog.setMinimumDuration(0); + + int total = m_files.size(); + std::vector> storage; + std::vector ptrs; + std::vector widths, heights; + + for (int i = 0; i < total; i++) + { + if (prog.wasCanceled()) { reject(); return; } + prog.setLabelText(QString("Loading image %1 / %2...").arg(i + 1).arg(total)); + prog.setValue((int)((float)(i + 1) / total * 40)); + QApplication::processEvents(); + + int w, h, ch; + uint8_t* px = stbi_load(m_files[i].toStdString().c_str(), &w, &h, &ch, 4); + if (!px) + { + QMessageBox::critical(this, "Error", "Failed to load: " + QFileInfo(m_files[i]).fileName()); + return; + } + std::vector rgba(px, px + (size_t)w * h * 4); + stbi_image_free(px); + widths.push_back(w); heights.push_back(h); + storage.push_back(std::move(rgba)); + } + + for (auto& v : storage) ptrs.push_back(v.data()); + + prog.setLabelText("Building sprite..."); + prog.setValue(50); + QApplication::processEvents(); + + ImageToSprParams p; + p.version = version; p.type = (uint32_t)type; + p.tex_format = (uint32_t)texFmt; p.interval = interval; + + auto result = SpriteConverter::RGBAToSprMemory(ptrs, widths, heights, p); + if (!result.success) + { + QMessageBox::critical(this, "Error", QString::fromStdString(result.error)); + return; + } + + prog.setLabelText("Saving..."); + prog.setValue(80); + QApplication::processEvents(); + + std::ofstream file(savePath.toStdString(), std::ios::binary); + if (!file.is_open()) + { + QMessageBox::critical(this, "Error", "Failed to create file"); + return; + } + file.write(reinterpret_cast(result.data.data()), (std::streamsize)result.data.size()); + file.close(); + + prog.setValue(100); + QMessageBox::information(this, "Success", QString("Created: %1\n%2 frame(s), %3 bytes").arg(QFileInfo(savePath).fileName()).arg(total).arg(result.data.size())); + + m_outputFile = savePath; + accept(); +} \ No newline at end of file diff --git a/src/import_dialog.h b/src/import_dialog.h new file mode 100644 index 0000000..2ecfee4 --- /dev/null +++ b/src/import_dialog.h @@ -0,0 +1,59 @@ +#pragma once + +#include "theme.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app_state.h" + +class ImportDialog : public QDialog +{ + Q_OBJECT +public: + ImportDialog(QWidget* parent, AppState& state); + QString outputFile() const { return m_outputFile; } + +private slots: + void onAddImages(); + void onClearAll(); + void onRemoveSelected(); + void onCreate(); + +private: + AppState& m_state; + QString m_outputFile; + QListWidget* m_fileList; + QComboBox* m_versionCombo; + QComboBox* m_typeCombo; + QComboBox* m_renderCombo; + QDoubleSpinBox* m_intervalSpin; + QStringList m_files; +}; + +class FileListItem : public QWidget { +public: + FileListItem(const QString& name, std::function onRemove) { + auto* l = new QHBoxLayout(this); + + auto* txt = new QLabel(name); + txt->setStyleSheet(QString("color: %1;").arg(SpriteColors::TextPrimary.name())); + + auto* btn = new QToolButton; + btn->setIcon(QIcon(":/icons/close.png")); + btn->setStyleSheet("border: none; background: transparent;"); + btn->setCursor(Qt::PointingHandCursor); + QObject::connect(btn, &QToolButton::clicked, onRemove); + + l->addWidget(txt, 1); + l->addWidget(btn); + } +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index acadb03..d02ce4e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,99 +1,21 @@ -#include - -#include "ui.h" - -#include "imgui.h" -#include "imgui_impl_opengl3.h" -#include "imgui_impl_glfw.h" - -#include - -static UI* g_ui = nullptr; - -static void DropCallback(GLFWwindow*, int count, const char** paths) -{ - if (g_ui && count > 0) - g_ui->SetPendingFile(paths[0]); -} +#include +#include "mainwindow.h" +#include "theme.h" int main(int argc, char* argv[]) { - glfwSetErrorCallback([](int err, const char* desc) - { - fprintf(stderr, "GLFW %d: %s\n", err, desc); - }); - - if (!glfwInit()) - return -1; - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - - GLFWwindow* window = glfwCreateWindow(1280, 720, "Sprite-Tools", nullptr, nullptr); - if (!window) - { - glfwTerminate(); - return -1; - } - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); + QApplication app(argc, argv); + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); + app.setApplicationName("Sprite-Tools"); + app.setPalette(Theme::darkPalette()); + app.setStyleSheet(Theme::globalStyleSheet()); - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init("#version 130"); - - UI ui; - g_ui = &ui; - - ui.LoadIcons(); - ui.SetupTheme(); - - glfwSetDropCallback(window, DropCallback); + MainWindow w; + w.resize(1280, 720); + w.show(); if (argc > 1) - ui.SetPendingFile(argv[1]); - - while (!glfwWindowShouldClose(window)) - { - glfwPollEvents(); - - ui.ProcessPendingFile(); - - if (ui.IsTitleChanged()) - glfwSetWindowTitle(window, ui.ConsumeTitle().c_str()); - - if (ui.IsExitRequested()) - glfwSetWindowShouldClose(window, GLFW_TRUE); - - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - ui.RenderFrame(); - - ImGui::Render(); - int dw, dh; - glfwGetFramebufferSize(window, &dw, &dh); - glViewport(0, 0, dw, dh); - glClearColor(0.08f, 0.08f, 0.10f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - glfwSwapBuffers(window); - } - - ui.CleanupResources(); - - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - glfwDestroyWindow(window); - glfwTerminate(); - g_ui = nullptr; + w.openFile(QString::fromLocal8Bit(argv[1])); - return 0; + return app.exec(); } \ No newline at end of file diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 0000000..9eba8da --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,708 @@ +#include "mainwindow.h" +#include "sprite_viewport.h" +#include "properties_panel.h" +#include "export_dialog.h" +#include "import_dialog.h" +#include "about_dialog.h" +#include "frame_slider.h" +#include "theme.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) +{ + setWindowTitle("Sprite-Tools"); + setAcceptDrops(true); + + m_viewport = new SpriteViewport(this); + setCentralWidget(m_viewport); + + createActions(); + createMenus(); + createToolBar(); + createStatusBar(); + createDockWidgets(); + + m_animTimer = new QTimer(this); + m_animTimer->setInterval(16); + connect(m_animTimer, &QTimer::timeout, this, &MainWindow::animTick); + + connect(this, &MainWindow::spriteLoaded, m_viewport, &SpriteViewport::onSpriteLoaded); + connect(this, &MainWindow::spriteClosed, m_viewport, &SpriteViewport::onSpriteClosed); + connect(this, &MainWindow::frameChanged, m_viewport, &SpriteViewport::onFrameChanged); + connect(this, &MainWindow::zoomChanged, m_viewport, &SpriteViewport::onZoomChanged); + + connect(this, &MainWindow::spriteLoaded, m_props, &PropertiesPanel::onSpriteLoaded); + connect(this, &MainWindow::spriteClosed, m_props, &PropertiesPanel::onSpriteClosed); + connect(this, &MainWindow::frameChanged, m_props, &PropertiesPanel::onFrameChanged); + + connect(m_viewport, &SpriteViewport::zoomRequested, this, &MainWindow::onZoomChanged); + + updatePlayback(); +} + +MainWindow::~MainWindow() +{ + if (m_state.sprite_loaded) + { + m_viewport->deleteTextures(); + m_loader.Unload(); + } +} + +QIcon MainWindow::icon(const QString& name) +{ + return QIcon(":/icons/" + name + ".png"); +} + +QToolButton* MainWindow::tinyBtn(const QString& iconName, const QString& tooltip, bool enabled, bool highlighted) +{ + auto* btn = new QToolButton; + QIcon ic = icon(iconName); + + btn->setProperty("origIcon", QVariant::fromValue(ic)); + + btn->setIcon(ic); + btn->setToolTip(tooltip); + btn->setIconSize(QSize(18, 18)); + btn->setFixedSize(34, 34); + btn->setAutoRaise(true); + + updateBtnState(btn, enabled, highlighted); + + return btn; +} + +void MainWindow::updateBtnState(QToolButton* btn, bool enabled, bool highlighted) +{ + btn->setEnabled(enabled); + + btn->setStyleSheet(QString(R"( + QToolButton { + border: 1px solid transparent; + border-radius: 6px; + padding: 4px; + background: transparent; + } + QToolButton:hover { + background-color: rgba(%1, %2, %3, 46); + border: 1px solid rgba(%1, %2, %3, 64); + } + QToolButton:pressed { + background-color: rgba(%1, %2, %3, 89); + } + QToolButton:disabled { + background: transparent; + border: none; + } + )").arg(SpriteColors::Accent.red()) + .arg(SpriteColors::Accent.green()) + .arg(SpriteColors::Accent.blue())); + + QColor tint; + if (!enabled) + { + tint = SpriteColors::TextDim; + } + else if (highlighted) + { + tint = SpriteColors::AccentLit; + } + else + { + tint = SpriteColors::TextPrimary; + } + + QIcon origIcon = btn->property("origIcon").value(); + if (origIcon.isNull()) + { + origIcon = btn->icon(); + btn->setProperty("origIcon", QVariant::fromValue(origIcon)); + } + + if (!origIcon.isNull()) + { + QPixmap pm = origIcon.pixmap(18, 18); + if (!pm.isNull()) + { + QPainter painter(&pm); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + painter.fillRect(pm.rect(), tint); + painter.end(); + btn->setIcon(QIcon(pm)); + } + } +} + +void MainWindow::createActions() +{ + m_actOpen = new QAction(icon("folder"), "&Open...", this); + m_actOpen->setShortcut(QKeySequence::Open); + connect(m_actOpen, &QAction::triggered, this, &MainWindow::onOpenFile); + + m_actClose = new QAction(icon("close"), "&Close", this); + m_actClose->setVisible(false); + connect(m_actClose, &QAction::triggered, this, &MainWindow::onCloseFile); + + m_actExport = new QAction(icon("save_alt"), "&Export Frames...", this); + m_actExport->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_E)); + m_actExport->setVisible(false); + connect(m_actExport, &QAction::triggered, this, &MainWindow::onExport); + + m_actImport = new QAction(icon("add_photo"), "&Import Images to SPR...", this); + m_actImport->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_I)); + connect(m_actImport, &QAction::triggered, this, &MainWindow::onImport); + + m_actExit = new QAction("E&xit", this); + m_actExit->setShortcut(QKeySequence(Qt::ALT | Qt::Key_F4)); + connect(m_actExit, &QAction::triggered, this, &QWidget::close); + + m_actShowToolbar = new QAction("Toolbar", this); + m_actShowToolbar->setCheckable(true); + m_actShowToolbar->setChecked(true); + connect(m_actShowToolbar, &QAction::toggled, this, [this](bool v) { + m_toolbar->setVisible(v); + m_state.show_toolbar = v; + }); + + m_actShowProps = new QAction("Properties", this); + m_actShowProps->setCheckable(true); + m_actShowProps->setChecked(true); + connect(m_actShowProps, &QAction::toggled, this, [this](bool v) { + m_propsDock->setVisible(v); + m_state.show_properties = v; + }); + + m_actShowChecker = new QAction("Transparency Grid", this); + m_actShowChecker->setCheckable(true); + m_actShowChecker->setChecked(true); + connect(m_actShowChecker, &QAction::toggled, this, [this](bool v) { + m_state.show_checker = v; + m_viewport->update(); + }); +} + +void MainWindow::createMenus() +{ + QMenu* file = menuBar()->addMenu("&File"); + file->addAction(m_actOpen); + file->addAction(m_actClose); + file->addSeparator(); + file->addAction(m_actExport); + file->addAction(m_actImport); + file->addSeparator(); + file->addAction(m_actExit); + + QMenu* view = menuBar()->addMenu("&View"); + view->addAction(m_actShowToolbar); + view->addAction(m_actShowProps); + view->addSeparator(); + view->addAction(m_actShowChecker); + view->addSeparator(); + + QMenu* zm = view->addMenu("Zoom"); + float zvals[] = {0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f}; + const char* zlbl[] = {"25%", "50%", "100%", "200%", "400%", "800%"}; + for (int i = 0; i < 6; i++) + { + float z = zvals[i]; + zm->addAction(zlbl[i], this, [this, z]() { onZoomChanged(z); }); + } + + m_menuPlayback = menuBar()->addMenu("&Playback"); + m_menuPlayback->addAction(icon("play"), "Play/Pause (Space)", this, &MainWindow::onPlayPause); + m_menuPlayback->addSeparator(); + m_menuPlayback->addAction(icon("skip_previous"), "First (Home)", this, &MainWindow::onFirstFrame); + m_menuPlayback->addAction(icon("navigate_before"),"Previous (Left)", this, &MainWindow::onPrevFrame); + m_menuPlayback->addAction(icon("navigate_next"), "Next (Right)", this, &MainWindow::onNextFrame); + m_menuPlayback->addAction(icon("skip_next"), "Last (End)", this, &MainWindow::onLastFrame); + + m_menuPlayback->menuAction()->setVisible(false); + + QMenu* help = menuBar()->addMenu("&Help"); + help->addAction(icon("info"), "About", this, &MainWindow::onAbout); +} + +void MainWindow::createToolBar() +{ + m_toolbar = addToolBar("Main"); + m_toolbar->setMovable(false); + m_toolbar->setIconSize(QSize(18, 18)); + m_toolbar->setStyleSheet(QString(R"( + QToolBar { + background-color: %1; + border-bottom: 1px solid %2; + spacing: 2px; + padding: 4px; + } + )").arg(SpriteColors::ToolbarBg.name(), SpriteColors::Border.name())); + + QString sliderStyle = QString(R"( + QSlider { min-height: 28px; max-height: 28px; } + QSlider::groove:horizontal { + height: 4px; background: %1; border-radius: 2px; + } + QSlider::sub-page:horizontal { + background: %2; border-radius: 2px; + } + QSlider::handle:horizontal { + background: %3; width: 14px; height: 14px; + margin: -5px 0; border-radius: 7px; + } + QSlider::handle:horizontal:hover { background: %4; } + QSlider::handle:horizontal:disabled { background: %5; } + QSlider::groove:horizontal:disabled { background: %6; } + QSlider::sub-page:horizontal:disabled { background: %6; } + )").arg(SpriteColors::BgLight.name(), SpriteColors::AccentDim.name(), + SpriteColors::Accent.name(), SpriteColors::AccentLit.name(), + SpriteColors::TextDim.name(), SpriteColors::BgDark.name()); + + m_btnFirst = tinyBtn("skip_previous", "First (Home)"); + m_btnPrev = tinyBtn("navigate_before", "Previous (Left)"); + m_btnPlayPause = tinyBtn("play", "Play/Pause (Space)"); + m_btnNext = tinyBtn("navigate_next", "Next (Right)"); + m_btnLast = tinyBtn("skip_next", "Last (End)"); + + connect(m_btnFirst, &QToolButton::clicked, this, &MainWindow::onFirstFrame); + connect(m_btnPrev, &QToolButton::clicked, this, &MainWindow::onPrevFrame); + connect(m_btnPlayPause, &QToolButton::clicked, this, &MainWindow::onPlayPause); + connect(m_btnNext, &QToolButton::clicked, this, &MainWindow::onNextFrame); + connect(m_btnLast, &QToolButton::clicked, this, &MainWindow::onLastFrame); + + m_toolbar->addWidget(m_btnFirst); + m_toolbar->addWidget(m_btnPrev); + m_toolbar->addWidget(m_btnPlayPause); + m_toolbar->addWidget(m_btnNext); + m_toolbar->addWidget(m_btnLast); + + m_speedLabel = new QLabel("1.0x"); + m_speedLabel->setFixedWidth(32); + m_speedLabel->setAlignment(Qt::AlignCenter); + m_speedLabel->setStyleSheet( + QString("font-size: 10px; color: %1;").arg(SpriteColors::TextDim.name())); + + m_speedSlider = new QSlider(Qt::Horizontal); + m_speedSlider->setRange(1, 80); + m_speedSlider->setValue(10); + m_speedSlider->setFixedWidth(60); + m_speedSlider->setStyleSheet(sliderStyle); + m_speedSlider->setToolTip("Playback speed"); + connect(m_speedSlider, &QSlider::valueChanged, this, [this](int v) { + m_state.anim_speed = v / 10.0f; + m_speedLabel->setText(QString::number(m_state.anim_speed, 'f', 1) + "x"); + }); + + m_toolbar->addWidget(m_speedLabel); + m_toolbar->addWidget(m_speedSlider); + + auto* spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + m_toolbar->addWidget(spacer); + + m_btnZoomOut = tinyBtn("zoom_out", "Zoom Out (-)"); + m_btnZoomIn = tinyBtn("zoom_in", "Zoom In (+)"); + + m_zoomLabel = new QLabel("100%"); + m_zoomLabel->setFixedWidth(45); + m_zoomLabel->setAlignment(Qt::AlignCenter); + m_zoomLabel->setStyleSheet(QString("color: %1; font-weight: 500; font-size: 11px;").arg(SpriteColors::TextPrimary.name())); + + m_btnZoomReset = tinyBtn("center_focus", "Reset Zoom (1)"); + + connect(m_btnZoomOut, &QToolButton::clicked, this, &MainWindow::onZoomOut); + connect(m_btnZoomIn, &QToolButton::clicked, this, &MainWindow::onZoomIn); + connect(m_btnZoomReset, &QToolButton::clicked, this, &MainWindow::onZoomReset); + + m_toolbar->addWidget(m_btnZoomOut); + m_toolbar->addWidget(m_btnZoomIn); + m_toolbar->addWidget(m_zoomLabel); + m_toolbar->addWidget(m_btnZoomReset); + + m_toolbar->addSeparator(); + + m_framePosLabel = new QLabel; + m_framePosLabel->setFixedWidth(48); + m_framePosLabel->setAlignment(Qt::AlignCenter); + m_framePosLabel->setStyleSheet(QString("font-size: 11px; color: %1;").arg(SpriteColors::TextDim.name())); + m_toolbar->addWidget(m_framePosLabel); + + m_frameSlider = new FrameSlider; + m_frameSlider->setToolTip("Current frame"); + connect(m_frameSlider, &FrameSlider::valueChanged, this, [this](int v) { + if (v != m_state.current_frame) setFrame(v); + }); + m_toolbar->addWidget(m_frameSlider); +} + +void MainWindow::createStatusBar() +{ + m_statusMsg = new QLabel; + m_statusInfo = new QLabel; + m_statusInfo->setStyleSheet(QString("font-size: 10px; color: %1;").arg(SpriteColors::TextDim.name())); + statusBar()->addWidget(m_statusMsg, 1); + statusBar()->addPermanentWidget(m_statusInfo); +} + +void MainWindow::createDockWidgets() +{ + m_props = new PropertiesPanel(this); + m_propsDock = new QDockWidget("Properties", this); + m_propsDock->setWidget(m_props); + + m_propsDock->setFeatures(QDockWidget::NoDockWidgetFeatures); + m_propsDock->setMinimumWidth(280); + + addDockWidget(Qt::RightDockWidgetArea, m_propsDock); + + connect(m_propsDock, &QDockWidget::visibilityChanged, + m_actShowProps, &QAction::setChecked); +} + +void MainWindow::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Space) + { + onPlayPause(); + event->accept(); + return; + } + if (event->key() == Qt::Key_Left) { onPrevFrame(); event->accept(); return; } + if (event->key() == Qt::Key_Right) { onNextFrame(); event->accept(); return; } + if (event->key() == Qt::Key_Home) { onFirstFrame(); event->accept(); return; } + if (event->key() == Qt::Key_End) { onLastFrame(); event->accept(); return; } + + if (event->key() == Qt::Key_Plus || event->key() == Qt::Key_Equal) + { onZoomIn(); event->accept(); return; } + if (event->key() == Qt::Key_Minus) + { onZoomOut(); event->accept(); return; } + if (event->key() == Qt::Key_1) + { onZoomReset(); event->accept(); return; } + + QMainWindow::keyPressEvent(event); +} + +void MainWindow::openFile(const QString& path) +{ + if (m_state.sprite_loaded) + { + m_viewport->deleteTextures(); + m_loader.Unload(); + m_state.sprite_loaded = false; + } + + std::string sp = path.toStdString(); + if (!m_loader.Load(sp)) + { + statusBar()->showMessage("Failed to load: " + QFileInfo(path).fileName(), 4000); + return; + } + + m_state.sprite_loaded = true; + m_state.filepath = sp; + m_state.fileName = QFileInfo(path).fileName().toStdString(); + m_state.current_frame = 0; + m_state.total_frames = m_loader.GetTotalFrameCount(); + m_state.animating = false; + m_state.anim_time = 0.0; + m_state.scroll_x = m_state.scroll_y = 0; + m_state.last_dir = QFileInfo(path).absolutePath().toStdString(); + + SpriteFrame* f = m_loader.GetFrame(0); + if (f) + { + float m = (float)std::max(f->width, f->height); + float z = (m < 64) ? 4.0f : (m < 128) ? 2.0f : 1.0f; + onZoomChanged(z); + } + + m_frameSlider->setRange(0, std::max(0, m_state.total_frames - 1)); + m_frameSlider->setValue(0); + + m_actClose->setEnabled(true); + m_actExport->setEnabled(true); + + updateTitle(); + updatePlayback(); + updateStatusInfo(); + + statusBar()->showMessage( + QString("Loaded: %1 (%2 frames)") + .arg(QFileInfo(path).fileName()) + .arg(m_state.total_frames), 4000); + + emit spriteLoaded(); +} + +void MainWindow::onOpenFile() +{ + QSettings settings("Sprite-Tools"); + QString lastDir = settings.value("lastDir", QString::fromStdString(m_state.last_dir)).toString(); + + QString p = QFileDialog::getOpenFileName(this, "Open Sprite", lastDir, "Sprite files (*.spr);;All (*)"); + + if (!p.isEmpty()) { + QString newDir = QFileInfo(p).absolutePath(); + settings.setValue("lastDir", newDir); + m_state.last_dir = newDir.toStdString(); + + openFile(p); + } +} + +void MainWindow::onCloseFile() +{ + if (!m_state.sprite_loaded) + return; + m_animTimer->stop(); + m_state.animating = false; + m_viewport->deleteTextures(); + m_loader.Unload(); + m_state.sprite_loaded = false; + m_state.current_frame = 0; + m_state.total_frames = 0; + m_actClose->setEnabled(false); + m_actExport->setEnabled(false); + m_frameSlider->setRange(0, 0); + updateTitle(); + updatePlayback(); + updateStatusInfo(); + statusBar()->showMessage("Closed", 3000); + emit spriteClosed(); + onZoomChanged(1.0f); +} + +void MainWindow::onExport() +{ + if (!m_state.sprite_loaded) + return; + ExportDialog dlg(this, m_state, m_loader); + dlg.exec(); +} + +void MainWindow::onImport() +{ + ImportDialog dlg(this, m_state); + if (dlg.exec() == QDialog::Accepted && !dlg.outputFile().isEmpty()) + openFile(dlg.outputFile()); +} + +void MainWindow::onAbout() +{ + AboutDialog dlg(this); + dlg.exec(); +} + +void MainWindow::onPlayPause() +{ + if (!m_state.sprite_loaded || m_state.total_frames <= 1) return; + m_state.animating = !m_state.animating; + if (m_state.animating) + { + m_elapsed.start(); + m_state.anim_time = 0.0; + m_animTimer->start(); + } + else + { + m_animTimer->stop(); + } + updatePlayback(); +} + +void MainWindow::onFirstFrame() +{ + setFrame(0); +} + +void MainWindow::onLastFrame() +{ + setFrame(std::max(0, m_state.total_frames - 1)); +} + +void MainWindow::onPrevFrame() +{ + if (m_state.total_frames <= 0) + return; + setFrame((m_state.current_frame - 1 + m_state.total_frames) % m_state.total_frames); +} + +void MainWindow::onNextFrame() +{ + if (m_state.total_frames <= 0) + return; + setFrame((m_state.current_frame + 1) % m_state.total_frames); +} + +void MainWindow::onZoomIn() +{ + onZoomChanged(std::min(16.0f, m_state.zoom * 2.0f)); +} + +void MainWindow::onZoomOut() +{ + onZoomChanged(std::max(0.25f, m_state.zoom * 0.5f)); +} + +void MainWindow::onZoomReset() +{ + m_state.scroll_x = m_state.scroll_y = 0; + + SpriteFrame* f = m_loader.GetFrame(0); + if (f) + { + float m = (float)std::max(f->width, f->height); + float z = (m < 64) ? 4.0f : (m < 128) ? 2.0f : 1.0f; + onZoomChanged(z); + } + else + { + onZoomChanged(1.0f); + } + +} + +void MainWindow::onZoomChanged(float z) +{ + m_state.zoom = std::clamp(z, 0.25f, 16.0f); + + if (m_zoomLabel) + { + m_zoomLabel->setText(QString("%1%").arg(qRound(m_state.zoom * 100))); + } + + emit zoomChanged(m_state.zoom); +} + +void MainWindow::setFrame(int f) +{ + if (f < 0 || f >= m_state.total_frames) return; + m_state.current_frame = f; + + m_frameSlider->blockSignals(true); + m_frameSlider->setValue(f); + m_frameSlider->blockSignals(false); + + m_framePosLabel->setText( + QString("%1/%2").arg(f + 1).arg(m_state.total_frames)); + + emit frameChanged(f); + updateStatusInfo(); +} + +void MainWindow::animTick() +{ + if (!m_state.animating || !m_state.sprite_loaded) return; + + double dt = m_elapsed.elapsed() / 1000.0; + m_elapsed.restart(); + m_state.anim_time += dt * m_state.anim_speed; + + SpriteFrame* fr = m_loader.GetFrame(m_state.current_frame); + if (!fr) return; + float iv = fr->interval > 0.0f ? fr->interval : 0.1f; + + while (m_state.anim_time >= iv) + { + m_state.anim_time -= iv; + m_state.current_frame = (m_state.current_frame + 1) % m_state.total_frames; + fr = m_loader.GetFrame(m_state.current_frame); + if (!fr) return; + iv = fr->interval > 0.0f ? fr->interval : 0.1f; + } + + m_frameSlider->blockSignals(true); + m_frameSlider->setValue(m_state.current_frame); + m_frameSlider->blockSignals(false); + + m_framePosLabel->setText( + QString("%1/%2").arg(m_state.current_frame + 1).arg(m_state.total_frames)); + + emit frameChanged(m_state.current_frame); + updateStatusInfo(); +} + +void MainWindow::updateTitle() +{ + setWindowTitle(m_state.sprite_loaded + ? QString::fromUtf8("Sprite-Tools \u2014 ") + + QFileInfo(QString::fromStdString(m_state.filepath)).fileName() + : "Sprite-Tools"); +} + +void MainWindow::updateStatusInfo() +{ + if (!m_state.sprite_loaded) { m_statusInfo->clear(); return; } + const SpriteData& d = m_loader.GetData(); + m_statusInfo->setText( + QString("v%1 %2 %3 frames") + .arg(d.version) + .arg(SpriteLoader::GetTexFormatString(d.texFormat)) + .arg(m_state.total_frames)); +} + +void MainWindow::updatePlayback() +{ + bool loaded = m_state.sprite_loaded; + bool multi = loaded && m_state.total_frames > 1; + bool playing = m_state.animating; + + m_actClose->setVisible(loaded); + m_actClose->setEnabled(loaded); + + m_actExport->setVisible(loaded); + m_actExport->setEnabled(loaded); + + + updateBtnState(m_btnFirst, multi, false); + updateBtnState(m_btnPrev, multi, false); + updateBtnState(m_btnNext, multi, false); + updateBtnState(m_btnLast, multi, false); + + QIcon playIcon = icon(playing ? "pause" : "play"); + m_btnPlayPause->setProperty("origIcon", QVariant::fromValue(playIcon)); + updateBtnState(m_btnPlayPause, multi, playing); + + updateBtnState(m_btnZoomOut, true, false); + updateBtnState(m_btnZoomIn, true, false); + updateBtnState(m_btnZoomReset, true, false); + + m_speedSlider->setEnabled(multi); + m_frameSlider->setEnabled(multi); + + if (loaded) + m_framePosLabel->setText(QString("%1/%2").arg(m_state.current_frame + 1).arg(m_state.total_frames)); + else + m_framePosLabel->clear(); +} + +void MainWindow::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasUrls()) + for (const QUrl& u : event->mimeData()->urls()) + if (u.toLocalFile().endsWith(".spr", Qt::CaseInsensitive)) + { event->acceptProposedAction(); return; } +} + +void MainWindow::dropEvent(QDropEvent* event) +{ + for (const QUrl& u : event->mimeData()->urls()) + { + QString p = u.toLocalFile(); + if (p.endsWith(".spr", Qt::CaseInsensitive)) + { + openFile(p); + return; + } + } +} \ No newline at end of file diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 0000000..e1c7aa1 --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app_state.h" +#include "sprite_loader.h" + +class SpriteViewport; +class PropertiesPanel; +class FrameSlider; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + + void openFile(const QString& path); + AppState& state() { return m_state; } + SpriteLoader& loader() { return m_loader; } + +signals: + void spriteLoaded(); + void spriteClosed(); + void frameChanged(int frame); + void zoomChanged(float zoom); + +public slots: + void onOpenFile(); + void onCloseFile(); + void onExport(); + void onImport(); + void onAbout(); + void onPlayPause(); + void onFirstFrame(); + void onLastFrame(); + void onPrevFrame(); + void onNextFrame(); + void onZoomIn(); + void onZoomOut(); + void onZoomReset(); + void onZoomChanged(float z); + void setFrame(int frame); + +protected: + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + +private: + QIcon icon(const QString& name); + QToolButton* tinyBtn(const QString& iconName, const QString& tooltip, bool enabled = true, bool highlighted = false); + void updateBtnState(QToolButton* btn, bool enabled, bool highlighted = false); + + void createActions(); + void createMenus(); + void createToolBar(); + void createStatusBar(); + void createDockWidgets(); + void updateTitle(); + void updateStatusInfo(); + void updatePlayback(); + void animTick(); + + AppState m_state; + SpriteLoader m_loader; + + SpriteViewport* m_viewport; + PropertiesPanel* m_props; + QDockWidget* m_propsDock; + + QToolBar* m_toolbar; + QSlider* m_speedSlider; + QLabel* m_speedLabel; + QLabel* m_framePosLabel; + FrameSlider* m_frameSlider; + + QToolButton* m_btnFirst; + QToolButton* m_btnPrev; + QToolButton* m_btnPlayPause; + QToolButton* m_btnNext; + QToolButton* m_btnLast; + QToolButton* m_btnZoomOut; + QToolButton* m_btnZoomIn; + QToolButton* m_btnZoomReset; + + QLabel* m_zoomLabel; + + QTimer* m_animTimer; + QElapsedTimer m_elapsed; + + QLabel* m_statusInfo; + QLabel* m_statusMsg; + + QAction* m_actOpen; + QAction* m_actClose; + QAction* m_actExport; + QAction* m_actImport; + QAction* m_actExit; + QAction* m_actShowToolbar; + QAction* m_actShowProps; + QAction* m_actShowChecker; + + QMenu* m_menuPlayback; +}; \ No newline at end of file diff --git a/src/properties_panel.cpp b/src/properties_panel.cpp new file mode 100644 index 0000000..3108558 --- /dev/null +++ b/src/properties_panel.cpp @@ -0,0 +1,219 @@ +#include "properties_panel.h" +#include "mainwindow.h" +#include "sprite_loader.h" +#include "theme.h" + +#include +#include +#include +#include +#include + +QWidget* sectionHeader(const QString& title) +{ + auto* w = new QWidget; + auto* lay = new QHBoxLayout(w); + lay->setContentsMargins(0, 12, 0, 4); + lay->setSpacing(8); + + auto* line1 = new QFrame; line1->setFrameShape(QFrame::HLine); + line1->setStyleSheet(QString("color: %1;").arg(SpriteColors::Border.name())); + line1->setFixedWidth(12); + + auto* lbl = new QLabel(title); + lbl->setStyleSheet(QString("font-size: 13px; font-weight: 500; color: %1;").arg(SpriteColors::TextSection.name())); + + auto* line2 = new QFrame; line2->setFrameShape(QFrame::HLine); + line2->setStyleSheet(QString("color: %1;").arg(SpriteColors::Border.name())); + + lay->addWidget(line1); + lay->addWidget(lbl); + lay->addWidget(line2); + return w; +} + +class PaletteWidget : public QWidget +{ +public: + PaletteWidget(const uint8_t* pal, int count, QWidget* parent = nullptr) + : QWidget(parent), m_pal(pal), m_count(count) + { + setMouseTracking(true); + int cols = 16, cs = 10; + int rows = (count + cols - 1) / cols; + setFixedSize(cols * (cs + 1), rows * (cs + 1)); + } + +protected: + void paintEvent(QPaintEvent*) override + { + QPainter p(this); + int cols = 16, cs = 10; + for (int i = 0; i < m_count && i < 256; i++) + { + int r = i / cols, c = i % cols; + p.fillRect(c * (cs + 1), r * (cs + 1), cs, cs, QColor(m_pal[i*3], m_pal[i*3+1], m_pal[i*3+2])); + } + } + + void mouseMoveEvent(QMouseEvent* e) override + { + int cols = 16, cs = 10; + int c = e->pos().x() / (cs + 1); + int r = e->pos().y() / (cs + 1); + int idx = r * cols + c; + if (idx >= 0 && idx < m_count) + { + uint8_t rv = m_pal[idx*3], g = m_pal[idx*3+1], b = m_pal[idx*3+2]; + QToolTip::showText(e->globalPosition().toPoint(), + QString("#%1: %2 %3 %4 (#%5%6%7)") + .arg(idx).arg(rv).arg(g).arg(b) + .arg(rv, 2, 16, QChar('0')) + .arg(g, 2, 16, QChar('0')) + .arg(b, 2, 16, QChar('0'))); + } + } + +private: + const uint8_t* m_pal; + int m_count; +}; + +PropertiesPanel::PropertiesPanel(MainWindow* mainWin, QWidget* parent) : QScrollArea(parent), m_main(mainWin) +{ + setWidgetResizable(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + m_content = new QWidget; + m_layout = new QVBoxLayout(m_content); + m_layout->setAlignment(Qt::AlignTop); + m_layout->setContentsMargins(10, 10, 10, 10); + m_layout->setSpacing(4); + + auto* ph = new QLabel("No file loaded"); + ph->setStyleSheet(QString("color: %1;").arg(SpriteColors::TextDim.name())); + ph->setAlignment(Qt::AlignCenter); + m_layout->addWidget(ph); + + setWidget(m_content); +} + +QLabel* PropertiesPanel::propLabel(const QString& name, const QString& value) +{ + return new QLabel(QString("%2 %3").arg(SpriteColors::TextDim.name(), name, value)); +} + +void PropertiesPanel::onSpriteLoaded() +{ + rebuild(); +} + +void PropertiesPanel::onSpriteClosed() +{ + QLayoutItem* item; + while ((item = m_layout->takeAt(0))) { delete item->widget(); delete item; } + auto* ph = new QLabel("No file loaded"); + ph->setStyleSheet(QString("color: %1;").arg(SpriteColors::TextDim.name())); + ph->setAlignment(Qt::AlignCenter); + m_layout->addWidget(ph); +} +void PropertiesPanel::onFrameChanged(int f) +{ + updateFrame(f); +} + +void PropertiesPanel::rebuild() +{ + QLayoutItem* item; + while ((item = m_layout->takeAt(0))) { delete item->widget(); delete item; } + + AppState& st = m_main->state(); + SpriteLoader& ld = m_main->loader(); + const SpriteData& d = ld.GetData(); + + auto* fileGrp = new QGroupBox("File"); + auto* fileL = new QVBoxLayout(fileGrp); + auto* fnLbl = new QLabel(QFileInfo(QString::fromStdString(d.filepath)).fileName()); + fnLbl->setToolTip(QString::fromStdString(d.filepath)); + fnLbl->setWordWrap(true); + fileL->addWidget(fnLbl); + m_layout->addWidget(fileGrp); + + auto* sprGrp = new QGroupBox("Sprite"); + auto* sprL = new QVBoxLayout(sprGrp); + const char* vn = (d.version == 1) ? "Quake" : (d.version == 2) ? "Half-Life" : "Unknown"; + sprL->addWidget(propLabel("Version:", QString("%1 (%2)").arg(d.version).arg(vn))); + sprL->addWidget(propLabel("Type:", SpriteLoader::GetTypeString(d.type))); + sprL->addWidget(propLabel("Render:", SpriteLoader::GetTexFormatString(d.texFormat))); + sprL->addWidget(propLabel("Cull:", SpriteLoader::GetFaceTypeString(d.facetype))); + sprL->addWidget(propLabel("Bounds:", QString("%1 x %2").arg(d.bounds[0]).arg(d.bounds[1]))); + sprL->addWidget(propLabel("Frames:", QString::number(st.total_frames))); + m_layout->addWidget(sprGrp); + + auto* frGrp = new QGroupBox("Frame"); + auto* frL = new QVBoxLayout(frGrp); + m_frameIndex = new QLabel; + m_frameSize = new QLabel; + m_frameOrigin = new QLabel; + m_frameInterval = new QLabel; + frL->addWidget(m_frameIndex); + frL->addWidget(m_frameSize); + frL->addWidget(m_frameOrigin); + frL->addWidget(m_frameInterval); + m_layout->addWidget(frGrp); + + auto* grpGrp = new QGroupBox("Groups"); + auto* grpL = new QVBoxLayout(grpGrp); + auto* tree = new QTreeWidget; + tree->setHeaderHidden(true); + tree->setMaximumHeight(200); + tree->setAlternatingRowColors(true); + + int gi = 0; + for (const auto& g : d.groups) + { + const char* ts = (g.type == FRAME_SINGLE) ? "Single" : (g.type == FRAME_GROUP) ? "Group" : "Angled"; + auto* gItem = new QTreeWidgetItem(tree); + gItem->setText(0, QString("%1 #%2 (%3)").arg(ts).arg(gi).arg(g.frames.size())); + for (int fi = 0; fi < (int)g.frames.size(); fi++) + { + const auto& f = g.frames[fi]; + auto* fItem = new QTreeWidgetItem(gItem); + fItem->setText(0, QString("%1x%2 (%3,%4) %5s") + .arg(f.width).arg(f.height) + .arg(f.origin[0]).arg(f.origin[1]) + .arg(f.interval, 0, 'f', 3)); + } + gi++; + } + grpL->addWidget(tree); + m_layout->addWidget(grpGrp); + + if (d.version == SPRITE_VERSION_HL && d.palette_colors > 0) + { + auto* palGrp = new QGroupBox("Palette"); + auto* palL = new QVBoxLayout(palGrp); + palL->addWidget(new PaletteWidget(d.palette, d.palette_colors)); + m_layout->addWidget(palGrp); + } + + m_layout->addStretch(); + updateFrame(st.current_frame); +} + +void PropertiesPanel::updateFrame(int frame) +{ + if (!m_frameIndex) + return; + + AppState& st = m_main->state(); + SpriteFrame* fr = m_main->loader().GetFrame(frame); + + if (!fr) + return; + + m_frameIndex->setText(propLabel("Index:", QString("%1 / %2").arg(frame + 1).arg(st.total_frames))->text()); + m_frameSize->setText(propLabel("Size:", QString("%1 x %2").arg(fr->width).arg(fr->height))->text()); + m_frameOrigin->setText(propLabel("Origin:", QString("%1, %2").arg(fr->origin[0]).arg(fr->origin[1]))->text()); + m_frameInterval->setText(propLabel("Interval:", QString("%1 s").arg(fr->interval, 0, 'f', 4))->text()); +} \ No newline at end of file diff --git a/src/properties_panel.h b/src/properties_panel.h new file mode 100644 index 0000000..3a84c8a --- /dev/null +++ b/src/properties_panel.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +class MainWindow; + +class PropertiesPanel : public QScrollArea +{ + Q_OBJECT + +public: + explicit PropertiesPanel(MainWindow* mainWin, QWidget* parent = nullptr); + +public slots: + void onSpriteLoaded(); + void onSpriteClosed(); + void onFrameChanged(int frame); + +private: + void rebuild(); + void updateFrame(int frame); + QLabel* propLabel(const QString& name, const QString& value); + + MainWindow* m_main; + QWidget* m_content = nullptr; + QVBoxLayout* m_layout = nullptr; + + QLabel* m_frameIndex = nullptr; + QLabel* m_frameSize = nullptr; + QLabel* m_frameOrigin = nullptr; + QLabel* m_frameInterval = nullptr; + QLabel* m_playingLabel = nullptr; +}; \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp deleted file mode 100644 index 0aa4e72..0000000 --- a/src/renderer.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "renderer.h" -#include - -void SpriteRenderer::UploadFrame(SpriteFrame& frame) -{ - if (frame.gl_texture != 0) - return; - - if (frame.rgba.empty() || frame.width <= 0 || frame.height <= 0) - return; - - GLuint tex; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame.width, frame.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame.rgba.data()); - glBindTexture(GL_TEXTURE_2D, 0); - - frame.gl_texture = tex; -} - -void SpriteRenderer::DeleteTextures(SpriteData& data) -{ - for (auto& group : data.groups) - { - for (auto& frame : group.frames) - { - if (frame.gl_texture != 0) - { - GLuint tex = frame.gl_texture; - glDeleteTextures(1, &tex); - frame.gl_texture = 0; - } - } - } -} - -void SpriteRenderer::UploadAllFrames(SpriteData& data) -{ - for (auto& group : data.groups) - { - for (auto& frame : group.frames) - { - UploadFrame(frame); - } - } -} \ No newline at end of file diff --git a/src/renderer.h b/src/renderer.h deleted file mode 100644 index a4cb560..0000000 --- a/src/renderer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "sprite_loader.h" - -#ifdef PLATFORM_WIN32 -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN -#include -#endif -#include - -class SpriteRenderer -{ -public: - SpriteRenderer() = default; - ~SpriteRenderer() = default; - - void UploadFrame(SpriteFrame& frame); - void DeleteTextures(SpriteData& data); - void UploadAllFrames(SpriteData& data); -}; diff --git a/src/sprite_viewport.cpp b/src/sprite_viewport.cpp new file mode 100644 index 0000000..5ec977c --- /dev/null +++ b/src/sprite_viewport.cpp @@ -0,0 +1,214 @@ +#include "sprite_viewport.h" +#include "mainwindow.h" +#include "sprite_loader.h" +#include "theme.h" + +#include +#include +#include +#include +#include +#include + +SpriteViewport::SpriteViewport(MainWindow* mainWin, QWidget* parent) : QWidget(parent), m_main(mainWin) +{ + setMouseTracking(true); + setFocusPolicy(Qt::StrongFocus); +} + +void SpriteViewport::rebuildImages() +{ + m_images.clear(); + SpriteLoader& loader = m_main->loader(); + int total = loader.GetTotalFrameCount(); + + for (int i = 0; i < total; i++) + { + SpriteFrame* f = loader.GetFrame(i); + if (!f || f->rgba.empty()) + { + m_images.emplace_back(); + continue; + } + QImage img(f->rgba.data(), f->width, f->height, + f->width * 4, QImage::Format_RGBA8888); + m_images.push_back(img.copy()); + } +} + +void SpriteViewport::drawChecker(QPainter& painter, const QRectF& rect) +{ + const float cell = 10.0f; + painter.save(); + painter.setClipRect(rect); + + QColor color1(24, 24, 32); + QColor color2(40, 40, 50); + + int startX = static_cast(rect.left() / cell); + int startY = static_cast(rect.top() / cell); + int endX = static_cast(rect.right() / cell) + 1; + int endY = static_cast(rect.bottom() / cell) + 1; + + for (int y = startY; y < endY; y++) { + for (int x = startX; x < endX; x++) { + QColor c = ((x + y) % 2 == 0) ? color1 : color2; + painter.fillRect(QRectF(x * cell, y * cell, cell, cell), c); + } + } + + painter.restore(); +} + + +void SpriteViewport::drawPlaceholder(QPainter& painter) +{ + QFont titleFont = painter.font(); + titleFont.setPointSize(28); + titleFont.setBold(true); + painter.setFont(titleFont); + painter.setPen(SpriteColors::Accent); + painter.drawText(rect(), Qt::AlignCenter, "Sprite-Tools"); + + QFont subFont = painter.font(); + subFont.setPointSize(12); + subFont.setBold(false); + painter.setFont(subFont); + painter.setPen(SpriteColors::TextDim); + + QRect sub = rect(); + sub.moveTop(sub.top() + 44); + painter.drawText(sub, Qt::AlignCenter, "Drop .spr file here or press Ctrl+O"); +} + +void SpriteViewport::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform, false); + painter.fillRect(rect(), SpriteColors::BgDark); + + AppState& s = m_main->state(); + + if (!s.sprite_loaded || m_images.empty()) + { + drawPlaceholder(painter); + return; + } + + int idx = s.current_frame; + if (idx < 0 || idx >= (int)m_images.size()) return; + + const QImage& img = m_images[idx]; + if (img.isNull()) + return; + + float iw = img.width() * s.zoom; + float ih = img.height() * s.zoom; + float vw = (float)width(); + float vh = (float)height(); + + float maxSx = std::max(0.0f, iw - vw); + float maxSy = std::max(0.0f, ih - vh); + s.scroll_x = std::clamp(s.scroll_x, 0.0f, maxSx); + s.scroll_y = std::clamp(s.scroll_y, 0.0f, maxSy); + + float ox = (vw > iw) ? (vw - iw) * 0.5f : -s.scroll_x; + float oy = (vh > ih) ? (vh - ih) * 0.5f : -s.scroll_y; + + QRectF sprRect(ox, oy, iw, ih); + + if (s.show_checker) + { + QRectF vis = sprRect.intersected(QRectF(0, 0, vw, vh)); + if (vis.isValid()) + drawChecker(painter, vis); + } + + painter.drawImage(sprRect, img); + + painter.setPen(QPen(SpriteColors::Border, 1.0)); + painter.setBrush(Qt::NoBrush); + painter.drawRect(sprRect); +} + +void SpriteViewport::onSpriteLoaded() +{ + rebuildImages(); + update(); +} + +void SpriteViewport::onSpriteClosed() +{ + m_images.clear(); + update(); +} + +void SpriteViewport::onFrameChanged(int) +{ + update(); +} + +void SpriteViewport::onZoomChanged(float) +{ + update(); +} + +void SpriteViewport::wheelEvent(QWheelEvent* event) +{ + float delta = event->angleDelta().y() / 120.0f; + AppState& s = m_main->state(); + + if (event->modifiers() & Qt::ControlModifier) + { + float oldZ = s.zoom; + float newZ = std::clamp(oldZ * (1.0f + delta * 0.15f), 0.25f, 16.0f); + float r = newZ / oldZ; + float cw = (float)width(), ch = (float)height(); + s.scroll_x = (s.scroll_x + cw * 0.5f) * r - cw * 0.5f; + s.scroll_y = (s.scroll_y + ch * 0.5f) * r - ch * 0.5f; + emit zoomRequested(newZ); + } + else if (event->modifiers() & Qt::ShiftModifier) + { + s.scroll_x -= delta * 50; + update(); + } + else + { + s.scroll_y -= delta * 50; + update(); + } + event->accept(); +} + +void SpriteViewport::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::MiddleButton) + { + m_dragging = true; + m_dragStart = event->pos(); + m_dragScrollX = m_main->state().scroll_x; + m_dragScrollY = m_main->state().scroll_y; + setCursor(Qt::ClosedHandCursor); + } +} + +void SpriteViewport::mouseMoveEvent(QMouseEvent* event) +{ + if (m_dragging) + { + QPoint d = event->pos() - m_dragStart; + m_main->state().scroll_x = m_dragScrollX - d.x(); + m_main->state().scroll_y = m_dragScrollY - d.y(); + update(); + } +} + +void SpriteViewport::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::MiddleButton && m_dragging) + { + m_dragging = false; + setCursor(Qt::ArrowCursor); + } +} \ No newline at end of file diff --git a/src/sprite_viewport.h b/src/sprite_viewport.h new file mode 100644 index 0000000..f19369e --- /dev/null +++ b/src/sprite_viewport.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +class MainWindow; + +class SpriteViewport : public QWidget +{ + Q_OBJECT + +public: + explicit SpriteViewport(MainWindow* mainWin, QWidget* parent = nullptr); + ~SpriteViewport() = default; + + void deleteTextures() { m_images.clear(); } + +signals: + void zoomRequested(float newZoom); + +public slots: + void onSpriteLoaded(); + void onSpriteClosed(); + void onFrameChanged(int frame); + void onZoomChanged(float zoom); + +protected: + void paintEvent(QPaintEvent* event) override; + void wheelEvent(QWheelEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + +private: + void rebuildImages(); + void drawChecker(QPainter& painter, const QRectF& rect); + void drawPlaceholder(QPainter& painter); + + MainWindow* m_main; + std::vector m_images; + + bool m_dragging = false; + QPoint m_dragStart; + float m_dragScrollX = 0; + float m_dragScrollY = 0; +}; \ No newline at end of file diff --git a/src/theme.h b/src/theme.h new file mode 100644 index 0000000..5229512 --- /dev/null +++ b/src/theme.h @@ -0,0 +1,393 @@ +#pragma once + +#include +#include +#include + +namespace SpriteColors +{ + inline const QColor BgDark {0x1C, 0x1C, 0x24}; + inline const QColor BgMid {0x24, 0x24, 0x2B}; + inline const QColor BgLight {0x2E, 0x2E, 0x38}; + inline const QColor BgLighter {0x38, 0x38, 0x45}; + inline const QColor ToolbarBg = BgMid; + inline const QColor StatusBg {0x17, 0x17, 0x1F}; + inline const QColor Accent {0x66, 0x99, 0xF2}; + inline const QColor AccentDim {0x4D, 0x73, 0xBF}; + inline const QColor AccentLit {0x8C, 0xB8, 0xFF}; + inline const QColor TextPrimary {0xE0, 0xE0, 0xEB}; + inline const QColor TextDim {0x80, 0x80, 0x94}; + inline const QColor TextSection {0x8C, 0xA6, 0xE6}; + inline const QColor Border {0x47, 0x47, 0x59, 0x80}; + inline const QColor Playing {0x73, 0xD9, 0x73}; + inline const QColor Success {0x4C, 0xAF, 0x50}; + inline const QColor Error {0xE5, 0x39, 0x35}; +} + +namespace Theme +{ + inline QString globalStyleSheet() + { + return QStringLiteral(R"( + * { + font-family: "Segoe UI", "Noto Sans", "Ubuntu", sans-serif; + } + QMainWindow, QDialog { + background-color: #1C1C24; + } + QToolTip { + color: #E0E0EB; + background-color: #21212B; + border: 1px solid #474759; + padding: 4px 8px; + border-radius: 4px; + } + QMenuBar { + background-color: #24242B; + border-bottom: 1px solid #3A3A48; + padding: 2px; + color: #E0E0EB; + } + QMenuBar::item { + padding: 4px 10px; + border-radius: 4px; + } + QMenuBar::item:selected { + background-color: rgba(102, 153, 242, 0.2); + } + QMenu { + background-color: #21212B; + border: 1px solid #3A3A48; + border-radius: 8px; + padding: 4px; + color: #E0E0EB; + } + QMenu::item { + padding: 6px 24px 6px 12px; + border-radius: 4px; + } + QMenu::item:selected { + background-color: rgba(102, 153, 242, 0.3); + } + QMenu::separator { + height: 1px; + background-color: #3A3A48; + margin: 4px 8px; + } + QMenu::icon { + padding-left: 8px; + } + QStatusBar { + background-color: #17171F; + border-top: 1px solid #3A3A48; + color: #808094; + } + QStatusBar QLabel { + padding: 2px 4px; + } + QToolBar { + background-color: #24242B; + border-bottom: 1px solid #3A3A48; + spacing: 2px; + padding: 2px 4px; + } + QToolBar QToolButton { + border: 1px solid transparent; + border-radius: 6px; + padding: 4px; + color: #E0E0EB; + } + QToolBar QToolButton:hover { + background-color: rgba(102, 153, 242, 0.18); + border: 1px solid rgba(102, 153, 242, 0.25); + } + QToolBar QToolButton:pressed { + background-color: rgba(102, 153, 242, 0.35); + } + QToolBar QToolButton:checked { + background-color: rgba(102, 153, 242, 0.25); + border: 1px solid rgba(102, 153, 242, 0.4); + } + QToolBar QToolButton:disabled { + color: #505060; + } + QToolBar QLabel { + color: #808094; + padding: 0 2px; + } + QDockWidget { + color: #8CA6E6; + font-weight: bold; + } + QDockWidget::title { + background-color: #24242B; + padding: 8px 12px; + border-bottom: 1px solid #3A3A48; + text-align: left; + } + QDockWidget::close-button, QDockWidget::float-button { + border: none; + padding: 2px; + } + QScrollBar:vertical { + background: #1C1C24; + width: 10px; + border: none; + } + QScrollBar::handle:vertical { + background: #505064; + min-height: 24px; + border-radius: 4px; + margin: 2px; + } + QScrollBar::handle:vertical:hover { + background: #6A6A86; + } + QScrollBar:horizontal { + background: #1C1C24; + height: 10px; + border: none; + } + QScrollBar::handle:horizontal { + background: #505064; + min-width: 24px; + border-radius: 4px; + margin: 2px; + } + QScrollBar::handle:horizontal:hover { + background: #6A6A86; + } + QScrollBar::add-line, QScrollBar::sub-line, + QScrollBar::add-page, QScrollBar::sub-page { + background: none; + height: 0px; + width: 0px; + } + QSlider::groove:horizontal { + height: 4px; + background: #3A3A48; + border-radius: 2px; + } + QSlider::handle:horizontal { + background: #6699F2; + width: 16px; + height: 16px; + margin: -6px 0; + border-radius: 8px; + } + QSlider::handle:horizontal:hover { + background: #88B4FF; + } + QSlider::handle:horizontal:disabled { + background: #505060; + } + QSlider::groove:horizontal:disabled { + background: #2E2E38; + } + QGroupBox { + font-weight: bold; + border: 1px solid #3A3A48; + border-radius: 8px; + margin-top: 12px; + padding: 12px 8px 8px 8px; + color: #8CA6E6; + } + QGroupBox::title { + subcontrol-origin: margin; + left: 12px; + padding: 0 6px; + } + QTreeWidget { + background-color: #24242C; + border: 1px solid #3A3A48; + border-radius: 6px; + alternate-background-color: #2A2A34; + color: #E0E0EB; + outline: none; + } + QTreeWidget::item { + padding: 3px 4px; + border-radius: 4px; + } + QTreeWidget::item:hover { + background-color: rgba(102, 153, 242, 0.12); + } + QTreeWidget::item:selected { + background-color: rgba(102, 153, 242, 0.25); + } + QHeaderView::section { + background-color: #24242C; + color: #808094; + border: none; + padding: 4px; + } + QComboBox { + background-color: #2E2E38; + border: 1px solid #3A3A48; + border-radius: 6px; + padding: 4px 10px; + color: #E0E0EB; + min-height: 28px; + } + QComboBox:hover { + border: 1px solid #6699F2; + } + QComboBox::drop-down { + border: none; + width: 24px; + } + QComboBox QAbstractItemView { + background-color: #21212B; + border: 1px solid #3A3A48; + border-radius: 6px; + color: #E0E0EB; + selection-background-color: rgba(102, 153, 242, 0.3); + } + QSpinBox, QDoubleSpinBox { + background-color: #2E2E38; + border: 1px solid #3A3A48; + border-radius: 6px; + padding: 4px 8px; + color: #E0E0EB; + min-height: 28px; + } + QSpinBox:hover, QDoubleSpinBox:hover { + border: 1px solid #6699F2; + } + QSpinBox::up-button, QSpinBox::down-button, + QDoubleSpinBox::up-button, QDoubleSpinBox::down-button { + border: none; + width: 20px; + } + QPushButton { + background-color: #2E2E38; + border: 1px solid #3A3A48; + border-radius: 8px; + padding: 6px 20px; + color: #E0E0EB; + min-height: 32px; + font-weight: 500; + } + QPushButton:hover { + background-color: rgba(102, 153, 242, 0.18); + border: 1px solid rgba(102, 153, 242, 0.4); + } + QPushButton:pressed { + background-color: rgba(102, 153, 242, 0.35); + } + QPushButton:disabled { + color: #505060; + background-color: #24242B; + border-color: #2E2E38; + } + QPushButton#primaryButton { + background-color: #4D73BF; + border: none; + color: white; + } + QPushButton#primaryButton:hover { + background-color: #6699F2; + } + QPushButton#primaryButton:pressed { + background-color: #3D5C99; + } + QProgressBar { + border: none; + border-radius: 4px; + text-align: center; + background-color: #2E2E38; + color: #E0E0EB; + min-height: 8px; + max-height: 8px; + } + QProgressBar::chunk { + background-color: #6699F2; + border-radius: 4px; + } + QRadioButton, QCheckBox { + color: #E0E0EB; + spacing: 8px; + } + QRadioButton::indicator, QCheckBox::indicator { + width: 18px; + height: 18px; + } + QLineEdit { + background-color: #2E2E38; + border: 1px solid #3A3A48; + border-radius: 6px; + padding: 4px 8px; + color: #E0E0EB; + min-height: 28px; + } + QLineEdit:hover { + border: 1px solid #6699F2; + } + QLineEdit:focus { + border: 2px solid #6699F2; + } + QListWidget { + background-color: #24242C; + border: 1px solid #3A3A48; + border-radius: 6px; + color: #E0E0EB; + outline: none; + } + QListWidget::item { + padding: 4px 8px; + border-radius: 4px; + } + QListWidget::item:hover { + background-color: rgba(102, 153, 242, 0.12); + } + QListWidget::item:selected { + background-color: rgba(102, 153, 242, 0.25); + } + QTabWidget::pane { + border: 1px solid #3A3A48; + border-radius: 6px; + background-color: #1C1C24; + } + QTabBar::tab { + background-color: #24242B; + color: #808094; + padding: 6px 16px; + border: none; + border-bottom: 2px solid transparent; + } + QTabBar::tab:selected { + color: #6699F2; + border-bottom: 2px solid #6699F2; + } + QTabBar::tab:hover { + color: #E0E0EB; + background-color: rgba(102, 153, 242, 0.1); + } + QDialog { + background-color: #24242B; + border-radius: 12px; + } + )"); + } + + inline QPalette darkPalette() + { + QPalette p; + p.setColor(QPalette::Window, SpriteColors::BgDark); + p.setColor(QPalette::WindowText, SpriteColors::TextPrimary); + p.setColor(QPalette::Base, SpriteColors::BgLight); + p.setColor(QPalette::AlternateBase, SpriteColors::BgLighter); + p.setColor(QPalette::ToolTipBase, SpriteColors::BgMid); + p.setColor(QPalette::ToolTipText, SpriteColors::TextPrimary); + p.setColor(QPalette::Text, SpriteColors::TextPrimary); + p.setColor(QPalette::Button, SpriteColors::BgLighter); + p.setColor(QPalette::ButtonText, SpriteColors::TextPrimary); + p.setColor(QPalette::BrightText, QColor(255, 80, 80)); + p.setColor(QPalette::Link, SpriteColors::Accent); + p.setColor(QPalette::Highlight, SpriteColors::Accent); + p.setColor(QPalette::HighlightedText, QColor(0, 0, 0)); + p.setColor(QPalette::Disabled, QPalette::Text, SpriteColors::TextDim); + p.setColor(QPalette::Disabled, QPalette::ButtonText, SpriteColors::TextDim); + return p; + } +} \ No newline at end of file diff --git a/src/ui.cpp b/src/ui.cpp deleted file mode 100644 index b74be81..0000000 --- a/src/ui.cpp +++ /dev/null @@ -1,1700 +0,0 @@ -#include "ui.h" -#include "sprite_converter.h" -#include "icons.h" - -ImGuiImage m_pCenterFocus; -ImGuiImage m_pFolder; -ImGuiImage m_pNavigateBefore; -ImGuiImage m_pNavigateNext; -ImGuiImage m_pPause; -ImGuiImage m_pPlay; -ImGuiImage m_pSkipNext; -ImGuiImage m_pSkipPrevious; -ImGuiImage m_pZoomIn; -ImGuiImage m_pZoomOut; - -#include -#include -#include -#include -#include - -#include "portable-file-dialogs.h" -#include "stb_image.h" - -void UI::LoadIcons() -{ - m_pCenterFocus = UI_utils::LoadImageFromMemory(center_focus_strong_png, center_focus_strong_png_len); - m_pFolder = UI_utils::LoadImageFromMemory(folder_png, folder_png_len); - m_pNavigateBefore = UI_utils::LoadImageFromMemory(navigate_before_png, navigate_before_png_len); - m_pNavigateNext = UI_utils::LoadImageFromMemory(navigate_next_png, navigate_next_png_len); - m_pPause = UI_utils::LoadImageFromMemory(pause_png, pause_png_len); - m_pPlay = UI_utils::LoadImageFromMemory(play_png, play_png_len); - m_pSkipNext= UI_utils::LoadImageFromMemory(skip_next_png, skip_next_png_len); - m_pSkipPrevious = UI_utils::LoadImageFromMemory(skip_previous_png, skip_previous_png_len); - m_pZoomIn = UI_utils::LoadImageFromMemory(zoom_in_png, zoom_in_png_len); - m_pZoomOut = UI_utils::LoadImageFromMemory(zoom_out_png, zoom_out_png_len); -} - -void UI::SetupTheme() -{ - ImGuiStyle& s = ImGui::GetStyle(); - - s.WindowRounding = 4.0f; - s.FrameRounding = 3.0f; - s.GrabRounding = 3.0f; - s.TabRounding = 3.0f; - s.ScrollbarRounding = 4.0f; - s.PopupRounding = 4.0f; - s.ChildRounding = 3.0f; - - s.WindowPadding = ImVec2(8, 8); - s.FramePadding = ImVec2(6, 3); - s.ItemSpacing = ImVec2(6, 4); - s.ItemInnerSpacing = ImVec2(4, 4); - s.WindowBorderSize = 1.0f; - s.FrameBorderSize = 0.0f; - s.PopupBorderSize = 1.0f; - s.ScrollbarSize = 12.0f; - s.GrabMinSize = 10.0f; - s.WindowTitleAlign = ImVec2(0.5f, 0.5f); - - ImVec4* c = s.Colors; - - ImVec4 bg_dark = ImVec4(0.11f, 0.11f, 0.14f, 1.00f); - ImVec4 bg_mid = ImVec4(0.14f, 0.14f, 0.17f, 1.00f); - ImVec4 bg_light = ImVec4(0.18f, 0.18f, 0.22f, 1.00f); - ImVec4 bg_lighter = ImVec4(0.22f, 0.22f, 0.27f, 1.00f); - - ImVec4 accent = ImVec4(0.40f, 0.60f, 0.95f, 1.00f); - ImVec4 accent_dim = ImVec4(0.30f, 0.45f, 0.75f, 1.00f); - ImVec4 accent_lit = ImVec4(0.55f, 0.72f, 1.00f, 1.00f); - - ImVec4 text = ImVec4(0.88f, 0.88f, 0.92f, 1.00f); - ImVec4 text_dim = ImVec4(0.50f, 0.50f, 0.58f, 1.00f); - - c[ImGuiCol_WindowBg] = bg_dark; - c[ImGuiCol_ChildBg] = ImVec4(0, 0, 0, 0); - c[ImGuiCol_PopupBg] = ImVec4(0.13f, 0.13f, 0.16f, 0.97f); - c[ImGuiCol_MenuBarBg] = bg_mid; - - c[ImGuiCol_TitleBg] = bg_dark; - c[ImGuiCol_TitleBgActive] = bg_mid; - c[ImGuiCol_TitleBgCollapsed] = bg_dark; - - c[ImGuiCol_Border] = ImVec4(0.28f, 0.28f, 0.35f, 0.50f); - c[ImGuiCol_BorderShadow] = ImVec4(0, 0, 0, 0); - - c[ImGuiCol_FrameBg] = bg_light; - c[ImGuiCol_FrameBgHovered] = bg_lighter; - c[ImGuiCol_FrameBgActive] = ImVec4(accent.x, accent.y, accent.z, 0.30f); - - c[ImGuiCol_Button] = bg_lighter; - c[ImGuiCol_ButtonHovered] = ImVec4(accent.x, accent.y, accent.z, 0.45f); - c[ImGuiCol_ButtonActive] = ImVec4(accent.x, accent.y, accent.z, 0.65f); - - c[ImGuiCol_Header] = ImVec4(accent.x, accent.y, accent.z, 0.18f); - c[ImGuiCol_HeaderHovered] = ImVec4(accent.x, accent.y, accent.z, 0.35f); - c[ImGuiCol_HeaderActive] = ImVec4(accent.x, accent.y, accent.z, 0.50f); - - c[ImGuiCol_Tab] = bg_mid; - c[ImGuiCol_TabHovered] = ImVec4(accent.x, accent.y, accent.z, 0.50f); - c[ImGuiCol_TabActive] = accent_dim; - c[ImGuiCol_TabUnfocused] = bg_dark; - c[ImGuiCol_TabUnfocusedActive] = bg_mid; - - c[ImGuiCol_SliderGrab] = accent_dim; - c[ImGuiCol_SliderGrabActive] = accent; - - c[ImGuiCol_CheckMark] = accent_lit; - - c[ImGuiCol_ScrollbarBg] = bg_dark; - c[ImGuiCol_ScrollbarGrab] = ImVec4(0.32f, 0.32f, 0.40f, 1.00f); - c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.42f, 0.42f, 0.52f, 1.00f); - c[ImGuiCol_ScrollbarGrabActive] = accent_dim; - - c[ImGuiCol_Separator] = ImVec4(0.28f, 0.28f, 0.35f, 0.60f); - c[ImGuiCol_SeparatorHovered] = accent_dim; - c[ImGuiCol_SeparatorActive] = accent; - - c[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.35f, 0.30f); - c[ImGuiCol_ResizeGripHovered] = accent_dim; - c[ImGuiCol_ResizeGripActive] = accent; - - c[ImGuiCol_Text] = text; - c[ImGuiCol_TextDisabled] = text_dim; - c[ImGuiCol_TextSelectedBg] = ImVec4(accent.x, accent.y, accent.z, 0.30f); - - c[ImGuiCol_DragDropTarget] = accent_lit; - c[ImGuiCol_NavHighlight] = accent; - c[ImGuiCol_PlotHistogram] = accent; - c[ImGuiCol_PlotHistogramHovered] = accent_lit; -} - -void UI::Tooltip(const char* text) -{ - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort)) - { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 6)); - ImGui::BeginTooltip(); - ImGui::TextUnformatted(text); - ImGui::EndTooltip(); - ImGui::PopStyleVar(); - } -} - -bool UI::ImgToolBtn(const char* id, ImGuiImage& img, const char* tip, bool active, bool enabled, float size) -{ - if (!enabled) - ImGui::BeginDisabled(); - - if (active) - { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.40f, 0.60f, 0.95f, 0.70f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.50f, 0.68f, 1.00f, 0.80f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.55f, 0.72f, 1.00f, 0.90f)); - } - - ImVec2 btn_size(size + 8, size + 8); - bool pressed = ImGui::ImageButton(id, (ImTextureID)(intptr_t)img.texture, ImVec2(size, size), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)); - - if (tip) - Tooltip(tip); - - if (active) - ImGui::PopStyleColor(3); - - if (!enabled) - ImGui::EndDisabled(); - - return pressed; -} - -void UI::ToolSep() -{ - ImGui::SameLine(0, 2); - ImVec2 p = ImGui::GetCursorScreenPos(); - float h = ImGui::GetFrameHeight(); - ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x + 1, p.y + 3), ImVec2(p.x + 1, p.y + h - 3), IM_COL32(255, 255, 255, 30), 1.0f); - ImGui::Dummy(ImVec2(4, h)); - ImGui::SameLine(0, 2); -} - -void UI::Section(const char* text) -{ - ImGui::Spacing(); - ImGui::Spacing(); - float w = ImGui::GetContentRegionAvail().x; - ImVec2 p = ImGui::GetCursorScreenPos(); - float text_w = ImGui::CalcTextSize(text).x; - - ImDrawList* dl = ImGui::GetWindowDrawList(); - float y = p.y + ImGui::GetTextLineHeight() * 0.5f; - - dl->AddLine(ImVec2(p.x, y), ImVec2(p.x + 6, y), IM_COL32(100, 100, 120, 100), 1.0f); - - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.55f, 0.65f, 0.90f, 1.0f)); - ImGui::TextUnformatted(text); - ImGui::PopStyleColor(); - - float after_text = p.x + 10 + text_w + 6; - dl->AddLine(ImVec2(after_text, y), ImVec2(p.x + w, y), IM_COL32(100, 100, 120, 100), 1.0f); - - ImGui::Spacing(); -} - -void UI::PropRow(const char* label, const char* fmt, ...) -{ - float label_w = 110.0f; - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.55f, 0.55f, 0.62f, 1.0f)); - ImGui::Text("%s", label); - ImGui::PopStyleColor(); - ImGui::SameLine(label_w); - - va_list args; - va_start(args, fmt); - ImGui::TextV(fmt, args); - va_end(args); -} - -std::string UI::GetDir(const std::string& path) -{ - size_t p = path.find_last_of("/\\"); - return (p != std::string::npos) ? path.substr(0, p) : ""; -} - -std::string UI::GetFilename(const std::string& path) -{ - size_t p = path.find_last_of("/\\"); - return (p != std::string::npos) ? path.substr(p + 1) : path; -} - -void UI::SetStatus(const std::string& msg) -{ - m_app.status_msg = msg; - auto now = std::chrono::high_resolution_clock::now(); - m_app.status_time = std::chrono::duration(now.time_since_epoch()).count(); -} - -void UI::OpenFileDialog() -{ - auto sel = pfd::open_file("Open Sprite", m_app.last_dir, { "Sprite files (*.spr)", "*.spr", "All files", "*" }, pfd::opt::none).result(); - - if (!sel.empty()) - m_app.pending_file = sel[0]; -} - -void UI::LoadSpriteFile(const std::string& path) -{ - if (m_app.sprite_loaded) - { - m_app.renderer.DeleteTextures(m_app.loader.GetData()); - m_app.loader.Unload(); - m_app.sprite_loaded = false; - } - - if (m_app.loader.Load(path)) - { - m_app.renderer.UploadAllFrames(m_app.loader.GetData()); - m_app.sprite_loaded = true; - m_app.current_frame = 0; - m_app.total_frames = m_app.loader.GetTotalFrameCount(); - m_app.animating = false; - m_app.anim_time = 0.0; - m_app.scroll_x = m_app.scroll_y = 0; - m_app.last_dir = GetDir(path); - - SpriteFrame* f = m_app.loader.GetFrame(0); - if (f) - { - float m = (float)std::max(f->width, f->height); - m_app.zoom = (m < 64) ? 4.0f : (m < 128) ? 2.0f : 1.0f; - } - - m_app.window_title = "Sprite-Tools — " + GetFilename(path); - m_app.title_changed = true; - - SetStatus("Loaded: " + GetFilename(path) + " (" + std::to_string(m_app.total_frames) + " frames)"); - } - else - { - SetStatus("Failed to load: " + GetFilename(path)); - } -} - -void UI::CloseSprite() -{ - if (!m_app.sprite_loaded) - return; - - m_app.renderer.DeleteTextures(m_app.loader.GetData()); - m_app.loader.Unload(); - m_app.sprite_loaded = false; - m_app.current_frame = 0; - m_app.total_frames = 0; - - m_app.window_title = "Sprite-Tools"; - m_app.title_changed = true; - - SetStatus("Closed"); -} - -void UI::SetPendingFile(const std::string& path) -{ - m_app.pending_file = path; -} - -void UI::ProcessPendingFile() -{ - if (m_app.pending_file.empty()) - return; - - LoadSpriteFile(m_app.pending_file); - m_app.pending_file.clear(); -} - -std::string UI::ConsumeTitle() -{ - m_app.title_changed = false; - return m_app.window_title; -} - -void UI::CleanupResources() -{ - if (m_app.sprite_loaded) - m_app.renderer.DeleteTextures(m_app.loader.GetData()); -} - -void UI::DrawMenuBar() -{ - if (!ImGui::BeginMainMenuBar()) - return; - - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Open", "Ctrl+O")) - OpenFileDialog(); - - if (ImGui::MenuItem("Close", nullptr, false, m_app.sprite_loaded)) - CloseSprite(); - - ImGui::Separator(); - - if (ImGui::MenuItem("Export Frames...", "Ctrl+E", - false, m_app.sprite_loaded)) - m_conv.show_export = true; - - if (ImGui::MenuItem("Import Images to SPR...", "Ctrl+I")) - m_conv.show_import = true; - - ImGui::Separator(); - - if (ImGui::MenuItem("Exit", "Alt+F4")) - m_app.request_exit = true; - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("View")) - { - ImGui::MenuItem("Toolbar", nullptr, &m_app.show_toolbar); - ImGui::MenuItem("Properties", nullptr, &m_app.show_info); - ImGui::Separator(); - ImGui::MenuItem("Transparency grid", nullptr, &m_app.show_checker); - - ImGui::Separator(); - - if (ImGui::BeginMenu("Zoom")) - { - const float zooms[] = { 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; - const char* labels[] = { "25%", "50%", "100%", "200%", "400%", "800%" }; - for (int i = 0; i < 6; i++) - { - bool sel = (std::abs(m_app.zoom - zooms[i]) < 0.01f); - if (ImGui::MenuItem(labels[i], nullptr, sel)) - { - m_app.zoom = zooms[i]; - m_app.scroll_x = m_app.scroll_y = 0; - } - } - ImGui::EndMenu(); - } - - ImGui::EndMenu(); - } - - bool can_play = m_app.sprite_loaded && m_app.total_frames > 1; - if (ImGui::BeginMenu("Playback", can_play)) - { - if (ImGui::MenuItem(m_app.animating ? "Pause" : "Play", "Space")) - { - m_app.animating = !m_app.animating; - if (m_app.animating) - { - auto now = std::chrono::high_resolution_clock::now(); - m_app.last_time = std::chrono::duration( - now.time_since_epoch()).count(); - } - } - ImGui::Separator(); - - if (ImGui::MenuItem("First", "Home")) - m_app.current_frame = 0; - if (ImGui::MenuItem("Previous", "Left")) - m_app.current_frame = (m_app.current_frame - 1 + m_app.total_frames) % m_app.total_frames; - if (ImGui::MenuItem("Next", "Right")) - m_app.current_frame = (m_app.current_frame + 1) % m_app.total_frames; - if (ImGui::MenuItem("Last", "End")) - m_app.current_frame = m_app.total_frames - 1; - - ImGui::Separator(); - ImGui::Text("Speed:"); - ImGui::SetNextItemWidth(120); - ImGui::SliderFloat("##spd", &m_app.anim_speed, 0.1f, 8.0f, "%.1fx"); - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Help")) - { - if (ImGui::MenuItem("About")) - m_app.show_about = true; - ImGui::EndMenu(); - } - - if (m_app.sprite_loaded) - { - SpriteFrame* f = m_app.loader.GetFrame(m_app.current_frame); - if (f) - { - char buf[96]; - snprintf(buf, sizeof(buf), "%dx%d | %d/%d | %.0f%%", f->width, f->height, m_app.current_frame + 1, m_app.total_frames, m_app.zoom * 100.0f); - float tw = ImGui::CalcTextSize(buf).x; - ImGui::SameLine(ImGui::GetWindowWidth() - tw - 16); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.50f, 0.50f, 0.58f, 1.0f)); - ImGui::TextUnformatted(buf); - ImGui::PopStyleColor(); - } - } - - ImGui::EndMainMenuBar(); -} - -void UI::DrawToolbar() -{ - if (!m_app.show_toolbar) - return; - - ImGuiViewport* vp = ImGui::GetMainViewport(); - float h = 38.0f; - float iconSize = 20.0f; - - ImGui::SetNextWindowPos(ImVec2(vp->WorkPos.x, vp->WorkPos.y)); - ImGui::SetNextWindowSize(ImVec2(vp->WorkSize.x, h)); - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 5)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3, 0)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.13f, 0.13f, 0.16f, 1.0f)); - - ImGui::Begin("##tb", nullptr, - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoScrollbar | - ImGuiWindowFlags_NoSavedSettings); - - bool loaded = m_app.sprite_loaded; - bool multi = loaded && m_app.total_frames > 1; - - if (ImgToolBtn("##open", m_pFolder, "Open (Ctrl+O)")) - OpenFileDialog(); - ImGui::SameLine(); - - ToolSep(); - - if (ImgToolBtn("##first", m_pSkipPrevious, "First (Home)", false, multi, iconSize)) - m_app.current_frame = 0; - ImGui::SameLine(); - - if (ImgToolBtn("##prev", m_pNavigateBefore, "Previous (Left)", false, multi, iconSize)) - m_app.current_frame = (m_app.current_frame - 1 + std::max(1, m_app.total_frames)) % std::max(1, m_app.total_frames); - ImGui::SameLine(); - { - bool playing = m_app.animating; - ImGuiImage& playIcon = playing ? m_pPause : m_pPlay; - if (ImgToolBtn("##playpause", playIcon, "Play/Pause (Space)", playing, multi, iconSize)) - { - m_app.animating = !m_app.animating; - if (m_app.animating) - { - auto now = std::chrono::high_resolution_clock::now(); - m_app.last_time = std::chrono::duration(now.time_since_epoch()).count(); - } - } - } - ImGui::SameLine(); - - if (ImgToolBtn("##next", m_pNavigateNext, "Next (Right)", false, multi, iconSize)) - m_app.current_frame = (m_app.current_frame + 1) % std::max(1, m_app.total_frames); - ImGui::SameLine(); - - if (ImgToolBtn("##last", m_pSkipNext, "Last (End)", false, multi, iconSize)) - m_app.current_frame = std::max(0, m_app.total_frames - 1); - - ImGui::SameLine(); - ToolSep(); - - ImGui::BeginDisabled(!multi); - ImGui::SetNextItemWidth(70); - ImGui::SliderFloat("##spd", &m_app.anim_speed, 0.1f, 8.0f, "%.1fx"); - Tooltip("Playback speed"); - ImGui::EndDisabled(); - - ImGui::SameLine(); - ToolSep(); - - if (ImgToolBtn("##zoomout", m_pZoomOut, "Zoom out (-)", false, true, iconSize)) - { - m_app.zoom = std::max(0.25f, m_app.zoom * 0.5f); - m_app.scroll_x = m_app.scroll_y = 0; - } - ImGui::SameLine(); - - ImGui::SetNextItemWidth(70); - float zoom_pct = m_app.zoom * 100.0f; - if (ImGui::SliderFloat("##zm", &zoom_pct, 25, 1600, "%.0f%%", ImGuiSliderFlags_Logarithmic)) - { - m_app.zoom = zoom_pct / 100.0f; - } - Tooltip("Zoom (Ctrl+Wheel)"); - ImGui::SameLine(); - - if (ImgToolBtn("##zoomin", m_pZoomIn, "Zoom in (+)", false, true, iconSize)) - m_app.zoom = std::min(16.0f, m_app.zoom * 2.0f); - ImGui::SameLine(); - - if (ImgToolBtn("##center", m_pCenterFocus, "100% (1)", false, true, iconSize)) - { - m_app.zoom = 1.0f; - m_app.scroll_x = m_app.scroll_y = 0; - } - - if (multi) - { - ImGui::SameLine(); - ToolSep(); - - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.55f, 0.55f, 0.62f, 1.0f)); - ImGui::Text("Frame"); - ImGui::PopStyleColor(); - ImGui::SameLine(); - - float remaining = ImGui::GetContentRegionAvail().x - 4; - ImGui::SetNextItemWidth(std::max(60.0f, remaining)); - int f = m_app.current_frame; - char frame_label[32]; - snprintf(frame_label, sizeof(frame_label), "%d / %d", f + 1, m_app.total_frames); - if (ImGui::SliderInt("##fr", &f, 0, m_app.total_frames - 1, frame_label, ImGuiSliderFlags_AlwaysClamp)) - m_app.current_frame = f; - } - - ImGui::End(); - ImGui::PopStyleColor(); - ImGui::PopStyleVar(2); -} - -void UI::DrawChecker(ImDrawList* dl, ImVec2 pos, ImVec2 sz, float cell) -{ - ImU32 a = IM_COL32(160, 160, 160, 255); - ImU32 b = IM_COL32(110, 110, 110, 255); - int nx = (int)(sz.x / cell) + 1; - int ny = (int)(sz.y / cell) + 1; - for (int y = 0; y < ny; y++) - for (int x = 0; x < nx; x++) - { - float x0 = pos.x + x * cell; - float y0 = pos.y + y * cell; - dl->AddRectFilled(ImVec2(x0, y0), ImVec2(std::min(x0 + cell, pos.x + sz.x), std::min(y0 + cell, pos.y + sz.y)), ((x + y) & 1) ? b : a); - } -} - -void UI::DrawViewport() -{ - float tb_h = m_app.show_toolbar ? 38.0f : 0.0f; - float sb_h = 26.0f; - float info_w = m_app.show_info ? 300.0f : 0.0f; - - ImGuiViewport* vp = ImGui::GetMainViewport(); - float vw = vp->WorkSize.x - info_w; - float vh = vp->WorkSize.y - tb_h - sb_h; - - ImGui::SetNextWindowPos(ImVec2(vp->WorkPos.x, vp->WorkPos.y + tb_h)); - ImGui::SetNextWindowSize(ImVec2(vw, vh)); - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.11f, 0.11f, 0.14f, 1.0f)); - - ImGui::Begin("##viewport", nullptr, - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoBringToFrontOnFocus | - ImGuiWindowFlags_NoScrollbar | - ImGuiWindowFlags_NoScrollWithMouse); - - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); - - if (!m_app.sprite_loaded) - { - ImVec2 ws = ImGui::GetWindowSize(); - - const char* t1 = "Sprite-Tools"; - - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.40f, 0.55f, 0.88f, 0.80f)); - float old_scale = ImGui::GetFont()->Scale; - ImGui::GetFont()->Scale = 1.5f; - ImGui::PushFont(ImGui::GetFont()); - ImVec2 ts_big = ImGui::CalcTextSize(t1); - ImGui::SetCursorPos(ImVec2((ws.x - ts_big.x) * 0.5f, ws.y * 0.36f)); - ImGui::TextUnformatted(t1); - ImGui::GetFont()->Scale = old_scale; - ImGui::PopFont(); - ImGui::PopStyleColor(); - - const char* t2 = "Drop .spr file here or press Ctrl+O"; - ImVec2 ts2 = ImGui::CalcTextSize(t2); - ImGui::SetCursorPos(ImVec2((ws.x - ts2.x) * 0.5f, ws.y * 0.36f + ts_big.y + 12)); - ImGui::TextDisabled("%s", t2); - - ImGui::End(); - return; - } - - SpriteFrame* frame = m_app.loader.GetFrame(m_app.current_frame); - if (!frame) - { - ImGui::End(); - return; - } - - if (m_app.animating && m_app.total_frames > 1) - { - auto now = std::chrono::high_resolution_clock::now(); - double t = std::chrono::duration(now.time_since_epoch()).count(); - m_app.anim_time += (t - m_app.last_time) * m_app.anim_speed; - m_app.last_time = t; - float iv = frame->interval > 0.0f ? frame->interval : 0.1f; - while (m_app.anim_time >= iv) - { - m_app.anim_time -= iv; - m_app.current_frame = (m_app.current_frame + 1) % m_app.total_frames; - frame = m_app.loader.GetFrame(m_app.current_frame); - if (!frame) { ImGui::End(); return; } - iv = frame->interval > 0.0f ? frame->interval : 0.1f; - } - } - - ImVec2 avail = ImGui::GetContentRegionAvail(); - float iw = frame->width * m_app.zoom; - float ih = frame->height * m_app.zoom; - - float sbt = 12.0f; - bool sx = iw > avail.x; - bool sy = ih > avail.y; - float cw = avail.x - (sy ? sbt : 0); - float ch = avail.y - (sx ? sbt : 0); - sx = iw > cw; sy = ih > ch; - cw = avail.x - (sy ? sbt : 0); - ch = avail.y - (sx ? sbt : 0); - - float msx = std::max(0.0f, iw - cw); - float msy = std::max(0.0f, ih - ch); - - if (ImGui::IsWindowHovered()) - { - float wh = ImGui::GetIO().MouseWheel; - if (wh != 0) - { - if (ImGui::GetIO().KeyCtrl) - { - float oz = m_app.zoom; - m_app.zoom = std::clamp(m_app.zoom * (1.0f + wh * 0.15f), 0.25f, 16.0f); - float r = m_app.zoom / oz; - m_app.scroll_x = (m_app.scroll_x + cw * 0.5f) * r - cw * 0.5f; - m_app.scroll_y = (m_app.scroll_y + ch * 0.5f) * r - ch * 0.5f; - } - else if (ImGui::GetIO().KeyShift) - m_app.scroll_x -= wh * 50; - else - m_app.scroll_y -= wh * 50; - } - if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) - { - m_app.dragging = true; - m_app.drag_start = ImGui::GetMousePos(); - m_app.drag_sx = m_app.scroll_x; - m_app.drag_sy = m_app.scroll_y; - } - } - if (m_app.dragging) - { - if (ImGui::IsMouseDown(ImGuiMouseButton_Middle)) - { - ImVec2 m = ImGui::GetMousePos(); - m_app.scroll_x = m_app.drag_sx - (m.x - m_app.drag_start.x); - m_app.scroll_y = m_app.drag_sy - (m.y - m_app.drag_start.y); - } - else - m_app.dragging = false; - } - - m_app.scroll_x = std::clamp(m_app.scroll_x, 0.0f, msx); - m_app.scroll_y = std::clamp(m_app.scroll_y, 0.0f, msy); - - ImVec2 wp = ImGui::GetCursorScreenPos(); - float ox = sx ? -m_app.scroll_x : (cw - iw) * 0.5f; - float oy = sy ? -m_app.scroll_y : (ch - ih) * 0.5f; - ImVec2 p0(wp.x + ox, wp.y + oy); - ImVec2 p1(p0.x + iw, p0.y + ih); - - ImDrawList* dl = ImGui::GetWindowDrawList(); - dl->PushClipRect(wp, ImVec2(wp.x + cw, wp.y + ch), true); - - if (m_app.show_checker) - { - float vx0 = std::max(p0.x, wp.x), vy0 = std::max(p0.y, wp.y); - float vx1 = std::min(p1.x, wp.x + cw), vy1 = std::min(p1.y, wp.y + ch); - if (vx0 < vx1 && vy0 < vy1) - DrawChecker(dl, ImVec2(vx0, vy0), ImVec2(vx1 - vx0, vy1 - vy0)); - } - - if (frame->gl_texture) - dl->AddImage((ImTextureID)(intptr_t)frame->gl_texture, p0, p1); - - dl->AddRect(p0, p1, IM_COL32(120, 140, 200, 60), 0, 0, 1.0f); - dl->PopClipRect(); - - auto DrawScrollbar = [&](bool horizontal) - { - float bar_x, bar_y, bar_w, bar_h; - float content_sz, img_sz, scroll_val, max_scroll; - - if (horizontal) - { - bar_x = wp.x; bar_y = wp.y + ch; - bar_w = cw; bar_h = sbt; - content_sz = cw; img_sz = iw; - scroll_val = m_app.scroll_x; max_scroll = msx; - } - else - { - bar_x = wp.x + cw; bar_y = wp.y; - bar_w = sbt; bar_h = ch; - content_sz = ch; img_sz = ih; - scroll_val = m_app.scroll_y; max_scroll = msy; - } - - dl->AddRectFilled(ImVec2(bar_x, bar_y), ImVec2(bar_x + bar_w, bar_y + bar_h), IM_COL32(20, 20, 25, 200), 2.0f); - - float vis = content_sz / img_sz; - float track = horizontal ? bar_w : bar_h; - float thumb = std::max(20.0f, track * vis); - float ratio = (max_scroll > 0) ? (scroll_val / max_scroll) : 0; - float thumb_pos = ratio * (track - thumb); - - ImVec2 tmin, tmax; - if (horizontal) - { - tmin = ImVec2(bar_x + thumb_pos, bar_y + 2); - tmax = ImVec2(bar_x + thumb_pos + thumb, bar_y + bar_h - 2); - } - else - { - tmin = ImVec2(bar_x + 2, bar_y + thumb_pos); - tmax = ImVec2(bar_x + bar_w - 2, bar_y + thumb_pos + thumb); - } - - bool hov = ImGui::IsMouseHoveringRect(tmin, tmax); - ImU32 tc = hov ? IM_COL32(140, 160, 220, 200) : IM_COL32(80, 80, 100, 180); - dl->AddRectFilled(tmin, tmax, tc, 3.0f); - - ImVec2 bmin(bar_x, bar_y), bmax(bar_x + bar_w, bar_y + bar_h); - if (ImGui::IsMouseHoveringRect(bmin, bmax) && ImGui::IsMouseClicked(0)) - { - float click = horizontal ? (ImGui::GetMousePos().x - bar_x) : (ImGui::GetMousePos().y - bar_y); - float& sv = horizontal ? m_app.scroll_x : m_app.scroll_y; - sv = (click / track) * max_scroll; - } - if (hov && ImGui::IsMouseDragging(0)) - { - float delta = horizontal ? ImGui::GetIO().MouseDelta.x : ImGui::GetIO().MouseDelta.y; - float& sv = horizontal ? m_app.scroll_x : m_app.scroll_y; - sv += delta * (max_scroll / (track - thumb)); - } - }; - - if (sx) - DrawScrollbar(true); - - if (sy) - DrawScrollbar(false); - - if (sx && sy) - dl->AddRectFilled( ImVec2(wp.x + cw, wp.y + ch), ImVec2(wp.x + cw + sbt, wp.y + ch + sbt), IM_COL32(20, 20, 25, 200), 2.0f); - - ImGui::End(); -} - -void UI::DrawProperties() -{ - if (!m_app.show_info) - return; - - float tb_h = m_app.show_toolbar ? 38.0f : 0.0f; - float sb_h = 26.0f; - float pw = 300.0f; - - ImGuiViewport* vp = ImGui::GetMainViewport(); - - ImGui::SetNextWindowPos(ImVec2(vp->WorkPos.x + vp->WorkSize.x - pw, vp->WorkPos.y + tb_h)); - ImGui::SetNextWindowSize(ImVec2(pw, vp->WorkSize.y - tb_h - sb_h)); - - ImGui::Begin("Properties", &m_app.show_info, - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoBringToFrontOnFocus); - - if (!m_app.sprite_loaded) - { - ImGui::TextDisabled("No file loaded"); - ImGui::End(); - return; - } - - const SpriteData& d = m_app.loader.GetData(); - - Section("File"); - { - std::string fn = GetFilename(d.filepath); - ImGui::TextUnformatted(fn.c_str()); - Tooltip(d.filepath.c_str()); - } - - Section("Sprite"); - { - const char* vn = (d.version == 1) ? "Quake" : (d.version == 2) ? "Half-Life" : "Unknown"; - PropRow("Version", "%d (%s)", d.version, vn); - PropRow("Type", "%s", SpriteLoader::GetTypeString(d.type)); - PropRow("Render", "%s", SpriteLoader::GetTexFormatString(d.texFormat)); - PropRow("Cull", "%s", SpriteLoader::GetFaceTypeString(d.facetype)); - PropRow("Bounds", "%d x %d", d.bounds[0], d.bounds[1]); - PropRow("Frames", "%d", m_app.total_frames); - } - - Section("Frame"); - SpriteFrame* fr = m_app.loader.GetFrame(m_app.current_frame); - if (fr) - { - PropRow("Index", "%d / %d", m_app.current_frame + 1, m_app.total_frames); - PropRow("Size", "%d x %d", fr->width, fr->height); - PropRow("Origin", "%d, %d", fr->origin[0], fr->origin[1]); - PropRow("Interval", "%.4f s", fr->interval); - - if (m_app.animating) - { - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.45f, 0.85f, 0.45f, 1.0f)); - ImGui::Text(" Playing"); - ImGui::PopStyleColor(); - } - } - - Section("Groups"); - int gi = 0; - for (const auto& g : d.groups) - { - const char* ts = (g.type == FRAME_SINGLE) ? "Single" : (g.type == FRAME_GROUP) ? "Group" : "Angled"; - char lbl[64]; - snprintf(lbl, sizeof(lbl), "%s #%d (%d)###g%d", ts, gi, (int)g.frames.size(), gi); - - if (ImGui::TreeNode(lbl)) - { - for (int fi = 0; fi < (int)g.frames.size(); fi++) - { - const auto& f = g.frames[fi]; - ImGui::BulletText("%dx%d (%d,%d) %.3fs", - f.width, f.height, f.origin[0], f.origin[1], f.interval); - } - ImGui::TreePop(); - } - gi++; - } - - if (d.version == SPRITE_VERSION_HL && d.palette_colors > 0) - { - Section("Palette"); - - ImVec2 cs(9, 9); - int cols = 16; - ImDrawList* pdl = ImGui::GetWindowDrawList(); - ImVec2 base = ImGui::GetCursorScreenPos(); - - for (int i = 0; i < d.palette_colors && i < 256; i++) - { - int row = i / cols, col = i % cols; - ImVec2 a(base.x + col * (cs.x + 1), base.y + row * (cs.y + 1)); - ImVec2 b(a.x + cs.x, a.y + cs.y); - - uint8_t r = d.palette[i * 3]; - uint8_t g = d.palette[i * 3 + 1]; - uint8_t bl = d.palette[i * 3 + 2]; - pdl->AddRectFilled(a, b, IM_COL32(r, g, bl, 255)); - - if (ImGui::IsMouseHoveringRect(a, b)) - { - pdl->AddRect(a, b, IM_COL32(255, 255, 100, 255), 0, 0, 1.5f); - ImGui::BeginTooltip(); - ImGui::ColorButton("##c",ImVec4(r / 255.f, g / 255.f, bl / 255.f, 1.f), ImGuiColorEditFlags_NoTooltip, ImVec2(28, 28)); - ImGui::SameLine(); - ImGui::Text("#%d\n%d %d %d\n#%02X%02X%02X", i, r, g, bl, r, g, bl); - ImGui::EndTooltip(); - } - } - - int rows = (std::min((int)d.palette_colors, 256) + cols - 1) / cols; - ImGui::Dummy(ImVec2(cols * (cs.x + 1), rows * (cs.y + 1))); - } - - ImGui::End(); -} - -void UI::DrawStatusBar() -{ - float h = 26.0f; - ImGuiViewport* vp = ImGui::GetMainViewport(); - - ImGui::SetNextWindowPos(ImVec2(vp->WorkPos.x, vp->WorkPos.y + vp->WorkSize.y - h)); - ImGui::SetNextWindowSize(ImVec2(vp->WorkSize.x, h)); - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 3)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.09f, 0.09f, 0.12f, 1.0f)); - - ImGui::Begin("##sb", nullptr, - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoScrollbar | - ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoBringToFrontOnFocus); - - if (!m_app.status_msg.empty()) - { - auto now = std::chrono::high_resolution_clock::now(); - double age = std::chrono::duration( - now.time_since_epoch()).count() - m_app.status_time; - if (age < 4.0) - { - float a = (age < 3.0) ? 1.0f : (float)(4.0 - age); - ImGui::PushStyleColor(ImGuiCol_Text, - ImVec4(0.75f, 0.80f, 0.92f, a)); - ImGui::TextUnformatted(m_app.status_msg.c_str()); - ImGui::PopStyleColor(); - } - else - m_app.status_msg.clear(); - } - - if (m_app.sprite_loaded) - { - const SpriteData& d = m_app.loader.GetData(); - char buf[96]; - snprintf(buf, sizeof(buf), "v%d %s %d frames", d.version, SpriteLoader::GetTexFormatString(d.texFormat), m_app.total_frames); - float tw = ImGui::CalcTextSize(buf).x; - ImGui::SameLine(ImGui::GetWindowWidth() - tw - 12); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.42f, 0.42f, 0.50f, 1.0f)); - ImGui::TextUnformatted(buf); - ImGui::PopStyleColor(); - } - - ImGui::End(); - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); -} - -void UI::DrawAbout() -{ - if (!m_app.show_about) - return; - - ImGui::OpenPopup("About##dlg"); - ImGui::SetNextWindowSize(ImVec2(400, 200), ImGuiCond_Appearing); - - if (ImGui::BeginPopupModal("About##dlg", &m_app.show_about, - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) - { - float footer_height = ImGui::GetFrameHeightWithSpacing() + 10.0f; - - ImGui::BeginChild("about_content", ImVec2(0, -footer_height), false); - - ImGui::Spacing(); - - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.45f, 0.62f, 0.95f, 1.0f)); - ImGui::Text("Sprite-Tools"); - ImGui::PopStyleColor(); - - ImGui::Spacing(); - ImGui::TextWrapped("Sprite viewer and creator for Quake / Half-Life sprites"); - - ImGui::Spacing(); - ImGui::Spacing(); - - ImGui::Text("Github:"); - ImGui::SameLine(); - - ImGui::TextColored(ImVec4(0.45f, 0.62f, 0.95f, 1.0f), "https://github.com/Elinsrc/Sprite-Tools"); - - if (ImGui::IsItemHovered()) - { - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - ImGui::SetTooltip("Open GitHub repository"); - - if (ImGui::IsItemClicked()) - { -#ifdef _WIN32 - ShellExecuteA(0, "open", "https://github.com/Elinsrc/Sprite-Tools", 0, 0, SW_SHOWNORMAL); -#else - system("xdg-open https://github.com/Elinsrc/Sprite-Tools"); -#endif - } - } - - ImGui::EndChild(); - - ImGui::Separator(); - - float button_width = 100.0f; - ImGui::SetCursorPosX((ImGui::GetWindowWidth() - button_width) * 0.5f); - - if (ImGui::Button("OK", ImVec2(button_width, 0))) - { - m_app.show_about = false; - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } -} -void UI::HandleKeys() -{ - ImGuiIO& io = ImGui::GetIO(); - - if (io.WantTextInput || ImGui::IsAnyItemActive()) - return; - - if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_O)) - { - OpenFileDialog(); - return; - } - - if (!m_app.sprite_loaded) - return; - - if (ImGui::IsKeyPressed(ImGuiKey_Space) && m_app.total_frames > 1) - { - m_app.animating = !m_app.animating; - if (m_app.animating) - { - auto now = std::chrono::high_resolution_clock::now(); - m_app.last_time = std::chrono::duration( - now.time_since_epoch()).count(); - } - } - - if (!m_app.animating && m_app.total_frames > 1) - { - if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) - m_app.current_frame = (m_app.current_frame - 1 + m_app.total_frames) % m_app.total_frames; - - if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) - m_app.current_frame = (m_app.current_frame + 1) % m_app.total_frames; - } - - if (ImGui::IsKeyPressed(ImGuiKey_Home)) - m_app.current_frame = 0; - - if (ImGui::IsKeyPressed(ImGuiKey_End)) - m_app.current_frame = std::max(0, m_app.total_frames - 1); - - if (ImGui::IsKeyPressed(ImGuiKey_Equal) || - ImGui::IsKeyPressed(ImGuiKey_KeypadAdd)) - m_app.zoom = std::min(16.0f, m_app.zoom * 2.0f); - - if (ImGui::IsKeyPressed(ImGuiKey_Minus) || - ImGui::IsKeyPressed(ImGuiKey_KeypadSubtract)) - m_app.zoom = std::max(0.25f, m_app.zoom * 0.5f); - - if (ImGui::IsKeyPressed(ImGuiKey_1)) - { - m_app.zoom = 1.0f; - m_app.scroll_x = m_app.scroll_y = 0; - } - - if (ImGui::IsKeyPressed(ImGuiKey_I)) - m_app.show_info = !m_app.show_info; - - if (ImGui::IsKeyPressed(ImGuiKey_T)) - m_app.show_toolbar = !m_app.show_toolbar; - - if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_E) && m_app.sprite_loaded) - { - m_conv.show_export = true; - return; - } - - if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_I)) - { - m_conv.show_import = true; - return; - } -} - -void UI::DrawExportDialog() -{ - if (!m_conv.show_export) - return; - - if (!m_app.sprite_loaded) - { - m_conv.show_export = false; - return; - } - - if (m_task.running.load()) - return; - - ImGui::OpenPopup("Export Frames##dlg"); - ImGui::SetNextWindowSize(ImVec2(360, 200), ImGuiCond_Appearing); - - if (ImGui::BeginPopupModal("Export Frames##dlg", &m_conv.show_export, ImGuiWindowFlags_NoResize)) - { - Section("Output Format"); - - const char* fmts[] = { "PNG (with alpha)", "BMP (no alpha)" }; - ImGui::Combo("Format", &m_conv.export_format, fmts, 2); - - Section("Frame Selection"); - - ImGui::RadioButton("All frames", &m_conv.export_frame, -1); - ImGui::SameLine(); - ImGui::RadioButton("Current only", &m_conv.export_frame, m_app.current_frame); - - ImGui::Spacing(); - ImGui::Separator(); - ImGui::Spacing(); - - float bw = 110; - ImGui::SetCursorPosX((ImGui::GetWindowWidth() - bw * 2 - 8) * 0.5f); - - if (ImGui::Button("Export...", ImVec2(bw, 0))) - { - ImGui::CloseCurrentPopup(); - StartExport(); - } - - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(bw, 0))) - { - m_conv.show_export = false; - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } -} - -void UI::DrawImportDialog() -{ - if (!m_conv.show_import) - return; - - if (m_task.running.load()) - return; - - ImGui::OpenPopup("Create SPR##dlg"); - ImGui::SetNextWindowSize(ImVec2(480, 420), ImGuiCond_Appearing); - - if (ImGui::BeginPopupModal("Create SPR##dlg", &m_conv.show_import, ImGuiWindowFlags_NoResize)) - { - Section("Input Images"); - - if (ImGui::Button("Add Images...")) - { - auto sel = pfd::open_file("Select Images", m_app.last_dir, { "Images", "*.png *.bmp", "All", "*" }, pfd::opt::multiselect).result(); - - for (const auto& s : sel) - m_conv.import_files.push_back(s); - } - ImGui::SameLine(); - if (ImGui::Button("Clear All")) - m_conv.import_files.clear(); - - ImGui::Text("%d file(s)", (int)m_conv.import_files.size()); - - if (!m_conv.import_files.empty()) - { - ImGui::BeginChild("##filelist", ImVec2(0, 90), ImGuiChildFlags_Borders); - - int rm = -1; - for (int i = 0; i < (int)m_conv.import_files.size(); i++) - { - ImGui::PushID(i); - if (ImGui::SmallButton("X")) rm = i; - ImGui::SameLine(); - ImGui::TextUnformatted(GetFilename(m_conv.import_files[i]).c_str()); - ImGui::PopID(); - } - if (rm >= 0) - m_conv.import_files.erase(m_conv.import_files.begin() + rm); - - ImGui::EndChild(); - } - - Section("Sprite Parameters"); - - const char* vers[] = { "Quake (v1)", "Half-Life (v2)" }; - int vi = m_conv.import_version - 1; - if (ImGui::Combo("Version", &vi, vers, 2)) - m_conv.import_version = vi + 1; - - const char* types[] = { - "Parallel Upright", - "Facing Upright", - "Parallel", - "Oriented", - "Parallel Oriented" - }; - ImGui::Combo("Type", &m_conv.import_type, types, 5); - - if (m_conv.import_version == 2) - { - const char* tfmts[] = { - "Normal", - "Additive", - "Index Alpha", - "Alpha Test" - }; - ImGui::Combo("Render Mode",&m_conv.import_tex_format, tfmts, 4); - } - - ImGui::SliderFloat("Interval", &m_conv.import_interval, 0.01f, 1.0f, "%.3f s"); - - ImGui::Spacing(); - ImGui::Separator(); - ImGui::Spacing(); - - bool can = !m_conv.import_files.empty(); - if (!can) - ImGui::BeginDisabled(); - - float bw = 110; - ImGui::SetCursorPosX((ImGui::GetWindowWidth() - bw * 2 - 8) * 0.5f); - - if (ImGui::Button("Create SPR...", ImVec2(bw, 0))) - { - ImGui::CloseCurrentPopup(); - StartImport(); - } - - if (!can) ImGui::EndDisabled(); - - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(bw, 0))) - { - m_conv.show_import = false; - m_conv.import_files.clear(); - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } -} - -void UI::StartTask(const std::string& title, std::function work) -{ - if (m_task.running.load()) - return; - - if (m_task.worker.joinable()) - m_task.worker.join(); - - m_task.running.store(true); - m_task.done.store(false); - m_task.cancel_requested.store(false); - m_task.progress.store(0.0f); - m_task.result_success = false; - m_task.pending_open_file.clear(); - - { - std::lock_guard lock(m_task.mutex); - m_task.title = title; - m_task.status = "Starting..."; - m_task.result_message.clear(); - } - - m_task.worker = std::thread([this, work]() - { - work(m_task); - - m_task.progress.store(1.0f); - m_task.running.store(false); - m_task.done.store(true); - }); -} - -void UI::DrawProgressDialog() -{ - if (!m_task.running.load() && !m_task.done.load()) - return; - - std::string title, status, result; - { - std::lock_guard lock(m_task.mutex); - title = m_task.title; - status = m_task.status; - result = m_task.result_message; - } - - float progress = m_task.progress.load(); - bool is_running = m_task.running.load(); - bool is_done = m_task.done.load(); - - std::string popup_id = title + "##progress"; - - ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(400, -1)); - - if (!ImGui::IsPopupOpen(popup_id.c_str())) - ImGui::OpenPopup(popup_id.c_str()); - - ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoSavedSettings; - - bool open = true; - if (ImGui::BeginPopupModal(popup_id.c_str(), nullptr, flags)) - { - if (is_running) - { - ImGui::TextWrapped("%s", status.c_str()); - ImGui::Spacing(); - - char pct[32]; - snprintf(pct, sizeof(pct), "%.0f%%", progress * 100.0f); - ImGui::ProgressBar(progress, ImVec2(-1, 0), pct); - - ImGui::Spacing(); - - double t = ImGui::GetTime(); - int dots = ((int)(t * 3.0)) % 4; - char anim[8] = " "; - for (int i = 0; i < dots; i++) anim[i] = '.'; - anim[dots] = '\0'; - - ImGui::TextDisabled("Working%s", anim); - - ImGui::Spacing(); - ImGui::Separator(); - ImGui::Spacing(); - - float bw = 120.0f; - ImGui::SetCursorPosX((ImGui::GetWindowWidth() - bw) * 0.5f); - if (ImGui::Button("Cancel", ImVec2(bw, 0))) - { - m_task.cancel_requested.store(true); - std::lock_guard lock(m_task.mutex); - m_task.status = "Cancelling..."; - } - } - else if (is_done) - { - if (m_task.result_success) - ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), "Success!"); - else - ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Failed!"); - - ImGui::Spacing(); - - if (!result.empty()) - { - ImGui::TextWrapped("%s", result.c_str()); - ImGui::Spacing(); - } - - ImGui::ProgressBar(1.0f, ImVec2(-1, 0), "Done"); - - ImGui::Spacing(); - ImGui::Separator(); - ImGui::Spacing(); - - float bw = 120.0f; - ImGui::SetCursorPosX((ImGui::GetWindowWidth() - bw) * 0.5f); - if (ImGui::Button("OK", ImVec2(bw, 0))) - { - m_task.done.store(false); - ImGui::CloseCurrentPopup(); - - if (!m_task.pending_open_file.empty()) - { - m_app.pending_file = m_task.pending_open_file; - m_task.pending_open_file.clear(); - } - - if (m_task.worker.joinable()) - m_task.worker.join(); - } - } - - ImGui::EndPopup(); - } -} - -void UI::StartExport() -{ - std::string spr_path = m_app.loader.GetData().filepath; - int format = m_conv.export_format; - int frame_idx = m_conv.export_frame; - int total = m_app.total_frames; - - auto dir = pfd::select_folder("Output Directory", m_app.last_dir).result(); - - if (dir.empty()) - return; - - std::string prefix = GetFilename(spr_path); - size_t dot = prefix.rfind('.'); - if (dot != std::string::npos) prefix = prefix.substr(0, dot); - - m_conv.show_export = false; - - StartTask("Exporting Frames", [=](TaskState& task) - { - SpriteLoader loader; - - { - std::lock_guard lock(task.mutex); - task.status = "Loading sprite..."; - } - - if (!loader.Load(spr_path)) - { - std::lock_guard lock(task.mutex); - task.result_message = "Failed to load sprite"; - task.result_success = false; - return; - } - - int t = loader.GetTotalFrameCount(); - int start = 0, end_idx = t; - - if (frame_idx >= 0 && frame_idx < t) - { - start = frame_idx; - end_idx = frame_idx + 1; - } - - int count = end_idx - start; - int exported = 0; - std::string ext = SpriteConverter::GetFormatExtension(static_cast(format)); - - for (int i = start; i < end_idx; i++) - { - if (task.cancel_requested.load()) - { - std::lock_guard lock(task.mutex); - task.result_message = "Cancelled. Exported " + - std::to_string(exported) + " file(s)"; - task.result_success = false; - return; - } - - { - std::lock_guard lock(task.mutex); - task.status = "Exporting frame " + - std::to_string(i + 1) + " / " + - std::to_string(end_idx) + "..."; - } - - SpriteFrame* frame = loader.GetFrame(i); - if (!frame || frame->rgba.empty()) - { - std::lock_guard lock(task.mutex); - task.result_message = "Failed to get frame " + std::to_string(i); - task.result_success = false; - return; - } - - char fname[512]; - if (t == 1 && frame_idx < 0) - snprintf(fname, sizeof(fname), "%s/%s%s", dir.c_str(), prefix.c_str(), ext.c_str()); - else - snprintf(fname, sizeof(fname), "%s/%s_%03d%s", dir.c_str(), prefix.c_str(), i, ext.c_str()); - - SprToImageParams p; - p.output_dir = dir; - p.output_prefix = prefix; - p.format = static_cast(format); - p.frame_index = i; - - if (!SpriteConverter::SaveImageRGBA(fname, frame->rgba.data(), frame->width, frame->height, static_cast(format))) - { - std::lock_guard lock(task.mutex); - task.result_message = "Failed to save: " + std::string(fname); - task.result_success = false; - return; - } - - exported++; - task.progress.store((float)(i - start + 1) / (float)count); - } - - std::lock_guard lock(task.mutex); - task.result_message = "Exported " + std::to_string(exported) + " file(s) to:\n" + dir; - task.result_success = true; - }); -} - -void UI::StartImport() -{ - auto save = pfd::save_file("Save Sprite", m_app.last_dir, { "Sprite (*.spr)", "*.spr" }).result(); - - if (save.empty()) return; - - if (save.size() < 4 || save.substr(save.size() - 4) != ".spr") - save += ".spr"; - - std::vector files = m_conv.import_files; - int version = m_conv.import_version; - int type = m_conv.import_type; - int tex_format = m_conv.import_tex_format; - float interval = m_conv.import_interval; - - m_conv.show_import = false; - m_conv.import_files.clear(); - - StartTask("Creating Sprite", [=](TaskState& task) - { - int total = (int)files.size(); - - std::vector> rgba_storage; - std::vector rgba_ptrs; - std::vector widths, heights; - - for (int i = 0; i < total; i++) - { - if (task.cancel_requested.load()) - { - std::lock_guard lock(task.mutex); - task.result_message = "Cancelled"; - task.result_success = false; - return; - } - - { - std::lock_guard lock(task.mutex); - task.status = "Loading image " + - std::to_string(i + 1) + " / " + - std::to_string(total) + "..."; - } - - std::vector rgba; - int w, h, ch; - uint8_t* px = stbi_load(files[i].c_str(), &w, &h, &ch, 4); - - if (!px) - { - std::lock_guard lock(task.mutex); - std::string fn = files[i]; - size_t sl = fn.find_last_of("/\\"); - if (sl != std::string::npos) fn = fn.substr(sl + 1); - task.result_message = "Failed to load: " + fn; - task.result_success = false; - return; - } - - rgba.assign(px, px + (size_t)w * h * 4); - stbi_image_free(px); - - if (w > 4096 || h > 4096 || w <= 0 || h <= 0) - { - std::lock_guard lock(task.mutex); - task.result_message = "Bad image size: " + - std::to_string(w) + "x" + std::to_string(h); - task.result_success = false; - return; - } - - widths.push_back(w); - heights.push_back(h); - rgba_storage.push_back(std::move(rgba)); - - task.progress.store((float)(i + 1) / (float)(total * 3)); - } - - if (task.cancel_requested.load()) - { - std::lock_guard lock(task.mutex); - task.result_message = "Cancelled"; - task.result_success = false; - return; - } - - for (auto& v : rgba_storage) - rgba_ptrs.push_back(v.data()); - - { - std::lock_guard lock(task.mutex); - task.status = "Building palette..."; - } - - task.progress.store(0.4f); - - ImageToSprParams p; - p.version = version; - p.type = (uint32_t)type; - p.tex_format = (uint32_t)tex_format; - p.interval = interval; - - auto result = SpriteConverter::RGBAToSprMemory(rgba_ptrs, widths, heights, p); - - if (task.cancel_requested.load()) - { - std::lock_guard lock(task.mutex); - task.result_message = "Cancelled"; - task.result_success = false; - return; - } - - task.progress.store(0.8f); - - if (!result.success) - { - std::lock_guard lock(task.mutex); - task.result_message = result.error; - task.result_success = false; - return; - } - - { - std::lock_guard lock(task.mutex); - task.status = "Saving sprite..."; - } - - std::ofstream file(save, std::ios::binary); - if (!file.is_open()) - { - std::lock_guard lock(task.mutex); - task.result_message = "Failed to create file"; - task.result_success = false; - return; - } - - file.write(reinterpret_cast(result.data.data()), - (std::streamsize)result.data.size()); - file.close(); - - task.progress.store(1.0f); - - std::string fn = save; - size_t sl = fn.find_last_of("/\\"); - if (sl != std::string::npos) fn = fn.substr(sl + 1); - - { - std::lock_guard lock(task.mutex); - task.result_message = "Created: " + fn + "\n" + - std::to_string(total) + " frame(s), " + - std::to_string(result.data.size()) + " bytes"; - task.result_success = true; - task.pending_open_file = save; - } - }); -} - -void UI::RenderFrame() -{ - HandleKeys(); - ProcessPendingFile(); - DrawMenuBar(); - DrawToolbar(); - DrawViewport(); - DrawProperties(); - DrawStatusBar(); - DrawAbout(); - DrawExportDialog(); - DrawImportDialog(); - DrawProgressDialog(); -} diff --git a/src/ui.h b/src/ui.h deleted file mode 100644 index 33d2a3a..0000000 --- a/src/ui.h +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "sprite_loader.h" -#include "renderer.h" - -#include "imgui.h" -#include "imgui_internal.h" - -#include "ui_utils.h" - -struct AppState -{ - SpriteLoader loader; - SpriteRenderer renderer; - - bool sprite_loaded = false; - int current_frame = 0; - int total_frames = 0; - - bool animating = false; - float anim_speed = 1.0f; - double anim_time = 0.0; - double last_time = 0.0; - - float zoom = 1.0f; - bool show_checker = true; - bool show_info = true; - bool show_toolbar = true; - bool show_about = false; - - float scroll_x = 0.0f; - float scroll_y = 0.0f; - bool dragging = false; - ImVec2 drag_start = ImVec2(0, 0); - float drag_sx = 0.0f; - float drag_sy = 0.0f; - - std::string pending_file; - std::string status_msg; - double status_time = 0.0; - std::string last_dir; - - bool request_exit = false; - std::string window_title; - bool title_changed = false; -}; - -class UI -{ -public: - UI() = default; - ~UI() = default; - - void LoadIcons(); - void SetupTheme(); - void RenderFrame(); - - void SetPendingFile(const std::string& path); - void ProcessPendingFile(); - - bool IsExitRequested() const { return m_app.request_exit; } - bool IsTitleChanged() const { return m_app.title_changed; } - std::string ConsumeTitle(); - - bool IsSpriteLoaded() const { return m_app.sprite_loaded; } - void CleanupResources(); - - AppState& GetState() { return m_app; } - const AppState& GetState() const { return m_app; } - -private: - void Tooltip(const char* text); - bool ImgToolBtn(const char* id, ImGuiImage& img, const char* tip = nullptr, bool active = false, bool enabled = true, float size = 20.0f); - void ToolSep(); - void Section(const char* text); - void PropRow(const char* label, const char* fmt, ...); - - void SetStatus(const std::string& msg); - void OpenFileDialog(); - void LoadSpriteFile(const std::string& path); - void CloseSprite(); - - void DrawMenuBar(); - void DrawToolbar(); - void DrawChecker(ImDrawList* dl, ImVec2 pos, ImVec2 sz, float cell = 12.0f); - void DrawViewport(); - void DrawProperties(); - void DrawStatusBar(); - void DrawAbout(); - void HandleKeys(); - - static std::string GetDir(const std::string& path); - static std::string GetFilename(const std::string& path); - - AppState m_app; - - void DrawExportDialog(); - void DrawImportDialog(); - - struct ConverterState - { - bool show_export = false; - bool show_import = false; - int export_format = 0; - int export_frame = -1; - - std::vector import_files; - int import_version = 2; - int import_type = 2; - int import_tex_format = 0; - float import_interval = 0.1f; - } m_conv; - - struct TaskState - { - std::atomic running{false}; - std::atomic done{false}; - std::atomic cancel_requested{false}; - std::atomic progress{0.0f}; - - std::mutex mutex; - std::string title; - std::string status; - std::string result_message; - bool result_success = false; - - std::string pending_open_file; - - std::thread worker; - } m_task; - - void DrawProgressDialog(); - void StartTask(const std::string& title, std::function work); - - void StartExport(); - void StartImport(); - -}; \ No newline at end of file diff --git a/src/ui_utils.cpp b/src/ui_utils.cpp deleted file mode 100644 index 5f5de2b..0000000 --- a/src/ui_utils.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "ui_utils.h" - -#include - -#ifdef PLATFORM_WIN32 -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN -#include -#endif -#include - -ImGuiImage UI_utils::LoadImageFromMemory(const unsigned char* buffer, int bufferSize) -{ - ImGuiImage result; - - int channels; - unsigned char* data = stbi_load_from_memory(buffer, bufferSize, &result.width, &result.height, &channels, 4); - - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, result.width, result.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - stbi_image_free(data); - - result.texture = (ImTextureID)(intptr_t)texture; - return result; -} \ No newline at end of file diff --git a/src/ui_utils.h b/src/ui_utils.h deleted file mode 100644 index 5f5f36a..0000000 --- a/src/ui_utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "imgui.h" - -struct ImGuiImage -{ - ImTextureID texture = 0; - int width = 0; - int height = 0; -}; - -class UI_utils -{ -public: - UI_utils() = default; - ~UI_utils() = default; - - static ImGuiImage LoadImageFromMemory(const unsigned char* buffer, int bufferSize); -}; From ee59a118a30de8b466d96eb51190b21604707489 Mon Sep 17 00:00:00 2001 From: Elinsrc <99191833+Elinsrc@users.noreply.github.com> Date: Wed, 18 Mar 2026 15:21:38 +0500 Subject: [PATCH 2/2] fix import dialog --- src/import_dialog.cpp | 81 +++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/src/import_dialog.cpp b/src/import_dialog.cpp index 6b3af81..7584b41 100644 --- a/src/import_dialog.cpp +++ b/src/import_dialog.cpp @@ -1,6 +1,5 @@ #include "import_dialog.h" #include "sprite_converter.h" - #include #include #include @@ -13,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -73,31 +74,20 @@ ImportDialog::ImportDialog(QWidget* parent, AppState& state) : QDialog(parent), lay->addSpacing(8); }; - auto* nameEd = new QLineEdit("output"); - nameEd->setStyleSheet(QString( - "QLineEdit { background: %1; color: %2; border: 1px solid %3; border-radius: 4px; padding: 4px; }" - "QLineEdit:focus { border: 1px solid %4; }" - ).arg(SpriteColors::BgLight.name(), SpriteColors::TextPrimary.name(), - SpriteColors::Border.name(), SpriteColors::Accent.name())); - addRow("Name:", nameEd); - auto* verW = new QWidget; auto* verL = new QHBoxLayout(verW); verL->setContentsMargins(0,0,0,0); auto* v1 = new QRadioButton("Quake (v1)"); auto* v2 = new QRadioButton("Half-Life (v2)"); - v2->setChecked(true); verL->addWidget(v1); verL->addWidget(v2); verL->addStretch(); m_versionCombo = new QComboBox; - m_versionCombo->addItem("", 1); m_versionCombo->addItem("", 2); + m_versionCombo->addItem("v1", 1); + m_versionCombo->addItem("v2", 2); m_versionCombo->setVisible(false); - connect(v1, &QRadioButton::toggled, [this](bool c) { if(c) m_versionCombo->setCurrentIndex(0); }); - connect(v2, &QRadioButton::toggled, [this](bool c) { if(c) m_versionCombo->setCurrentIndex(1); }); - addRow("Version:", verW); m_typeCombo = new QComboBox; @@ -114,27 +104,43 @@ ImportDialog::ImportDialog(QWidget* parent, AppState& state) : QDialog(parent), intL->setContentsMargins(0,0,0,0); m_intervalSpin = new QDoubleSpinBox; - m_intervalSpin->setRange(0.01, 1.0); m_intervalSpin->setValue(0.1); + m_intervalSpin->setRange(0.01, 1.0); m_intervalSpin->setVisible(false); auto* intSl = new QSlider(Qt::Horizontal); - intSl->setRange(1, 100); intSl->setValue(10); + intSl->setRange(1, 100); intSl->setFixedHeight(40); auto* intLbl = new QLabel("0.10s"); intLbl->setFixedWidth(40); intLbl->setStyleSheet(QString("color: %1;").arg(SpriteColors::TextPrimary.name())); - connect(intSl, &QSlider::valueChanged, [=](int v) { - float val = v / 100.0f; - m_intervalSpin->setValue(val); - intLbl->setText(QString::number(val, 'f', 2) + "s"); - }); - intL->addWidget(intSl); intL->addWidget(intLbl); addRow("Interval:", intW); - connect(m_versionCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int) { m_renderCombo->setEnabled(m_versionCombo->currentData().toInt() == 2); }); + connect(v1, &QRadioButton::toggled, [this](bool c) { + if(c) { + m_versionCombo->setCurrentIndex(0); + m_renderCombo->setCurrentIndex(0); + m_renderCombo->setEnabled(false); + } + }); + connect(v2, &QRadioButton::toggled, [this](bool c) { + if(c) { + m_versionCombo->setCurrentIndex(1); + m_renderCombo->setEnabled(true); + } + }); + + connect(intSl, &QSlider::valueChanged, [this, intLbl](int v) { + float val = v / 100.0f; + m_intervalSpin->setValue((double)val); + intLbl->setText(QString::number(val, 'f', 2) + "s"); + }); + + v2->setChecked(true); + intSl->setValue(10); + m_intervalSpin->setValue(0.1); lay->addStretch(); @@ -162,10 +168,7 @@ ImportDialog::ImportDialog(QWidget* parent, AppState& state) : QDialog(parent), connect(addBtn, &QPushButton::clicked, this, &ImportDialog::onAddImages); connect(clrBtn, &QPushButton::clicked, this, &ImportDialog::onClearAll); - connect(createBtn, &QPushButton::clicked, [=]() { - m_outputFile = nameEd->text(); - this->onCreate(); - }); + connect(createBtn, &QPushButton::clicked, this, &ImportDialog::onCreate); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); } @@ -173,24 +176,22 @@ void ImportDialog::onAddImages() { QSettings settings("Sprite-Tools"); QString lastDir = settings.value("lastImportDir", QString::fromStdString(m_state.last_dir)).toString(); - QStringList files = QFileDialog::getOpenFileNames(this, "Select Images", lastDir, "Images (*.png *.bmp);;All (*)"); if (files.isEmpty()) return; QString newDir = QFileInfo(files.first()).absolutePath(); - + m_state.last_dir = newDir.toStdString(); - + settings.setValue("lastImportDir", newDir); for (const auto& f : files) { m_files.append(f); - auto* item = new QListWidgetItem(m_fileList); - auto* w = new FileListItem(QFileInfo(f).fileName(), [this, item, f]() { + auto* w = new FileListItem(QFileInfo(f).fileName(), [this, item]() { int row = m_fileList->row(item); m_files.removeAt(row); delete m_fileList->takeItem(row); @@ -229,11 +230,13 @@ void ImportDialog::onCreate() if (savePath.isEmpty()) return; if (!savePath.endsWith(".spr", Qt::CaseInsensitive)) savePath += ".spr"; - int version = m_versionCombo->currentData().toInt(); + int version = m_versionCombo->itemData(m_versionCombo->currentIndex()).toInt(); int type = m_typeCombo->currentIndex(); int texFmt = m_renderCombo->currentIndex(); float interval = (float)m_intervalSpin->value(); + if (version == 1) texFmt = 0; + QProgressDialog prog("Creating sprite...", "Cancel", 0, 100, this); prog.setWindowTitle("Creating Sprite"); prog.setWindowModality(Qt::WindowModal); @@ -258,10 +261,10 @@ void ImportDialog::onCreate() QMessageBox::critical(this, "Error", "Failed to load: " + QFileInfo(m_files[i]).fileName()); return; } - std::vector rgba(px, px + (size_t)w * h * 4); + storage.emplace_back(px, px + (size_t)w * h * 4); stbi_image_free(px); - widths.push_back(w); heights.push_back(h); - storage.push_back(std::move(rgba)); + widths.push_back(w); + heights.push_back(h); } for (auto& v : storage) ptrs.push_back(v.data()); @@ -271,8 +274,10 @@ void ImportDialog::onCreate() QApplication::processEvents(); ImageToSprParams p; - p.version = version; p.type = (uint32_t)type; - p.tex_format = (uint32_t)texFmt; p.interval = interval; + p.version = version; + p.type = (uint32_t)type; + p.tex_format = (uint32_t)texFmt; + p.interval = interval; auto result = SpriteConverter::RGBAToSprMemory(ptrs, widths, heights, p); if (!result.success)