diff options
65 files changed, 3818 insertions, 2091 deletions
diff --git a/.gitmodules b/.gitmodules index 6492272..c113260 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "depends/jsoncpp"] path = depends/jsoncpp url = git://git.dec05eba.com/jsoncpp +[submodule "depends/mglpp"] + path = depends/mglpp + url = git://git.dec05eba.com/mglpp @@ -1,8 +1,6 @@ # QuickMedia A rofi inspired native client for web services. -Currently supported web services: `youtube`, `peertube`, `lbry`, `soundcloud`, `nyaa.si`, `manganelo`, `manganelos`, `mangatown`, `mangakatana`, `mangadex`, `readm`, `onimanga`, `4chan`, `matrix`, `saucenao`, `hotexamples`, `anilist` and _others_.\ -Config data, including manga progress is stored under `$XDG_CONFIG_HOME/quickmedia` or `$HOME/.config/quickmedia`.\ -Cache is stored under `$XDG_CACHE_HOME/quickmedia` or `$HOME/.cache/quickmedia`. +Currently supported web services: `youtube`, `peertube`, `lbry`, `soundcloud`, `nyaa.si`, `manganelo`, `manganelos`, `mangatown`, `mangakatana`, `mangadex`, `readm`, `onimanga`, `4chan`, `matrix`, `saucenao`, `hotexamples`, `anilist` and _others_. ## Usage ``` usage: quickmedia [plugin] [--dir <directory>] [-e <window>] [youtube-url] @@ -26,7 +24,7 @@ EXAMPLES: If you are running arch linux then you can install QuickMedia from aur (https://aur.archlinux.org/packages/quickmedia-git/), otherwise you will need to first install [sibs](https://git.dec05eba.com/sibs/) and then run `./install.sh` as root. ## Dependencies ### Libraries -`sfml`, `libx11`, `libxrandr` +`libglvnd (LibGL.so)`, `libx11`, `libxrandr` ### Executables `curl` ### Fonts @@ -154,10 +152,10 @@ Type text and then wait and QuickMedia will automatically search.\ `/me [text]`: Send a message of type "m.emote".\ `/react [text]`: React to the selected message (also works if you are replying to a message). ## Config -Config is loaded from `$HOME/quickmedia/config.json` if it exists. See [example-config.json](https://git.dec05eba.com/QuickMedia/plain/example-config.json) for an example config. All fields in the config file are optional.\ +Config is loaded from `~/.config/quickmedia/config.json` if it exists. See [example-config.json](https://git.dec05eba.com/QuickMedia/plain/example-config.json) for an example config. All fields in the config file are optional.\ If `use_system_mpv_config` is set to `true` then your systems mpv config in `~/.config/mpv/mpv.conf` and plugins will be used. If you have a mpv plugin installed that uses `input-ipc-server` (such as `mpv-discord`) then it will break quickmedia integration with mpv (especially key inputs such as ctrl+r). ## Theme -Theme is loaded from `$HOME/quickmedia/themes/<theme-name>.json` if it exists or from `/usr/share/quickmedia/themes`. Theme name is set in `$HOME/quickmedia/config.json` under the variable `theme`.\ +Theme is loaded from `~/.config/quickmedia/themes/<theme-name>.json` if it exists or from `/usr/share/quickmedia/themes`. Theme name is set in `~/.config/quickmedia/config.json` under the variable `theme`.\ Default themes available: `default, nord`.\ See [default.json](https://git.dec05eba.com/QuickMedia/plain/themes/default.json) for an example theme. ## Environment variables @@ -166,7 +164,10 @@ Set `QM_PHONE_FACTOR=1` to disable the room list side panel in matrix. ## UI scaling Set `GDK_SCALE` environment variable or add `Xft.dpi` to `$HOME/.Xresources`. For example a value of 96 for the `Xft.dpi` means 1.0 scaling and 144 (96*1.5) means 1.5 scaling.\ Note that at the moment, images do also not scale above their original size.\ -Scaling can also be set in `$HOME/quickmedia/config.json`, which will override `GDK_SCALE` and `$HOME/.Xresources` `Xft.dpi`. +Scaling can also be set in `~/.config/quickmedia/config.json`, which will override `GDK_SCALE` and `$HOME/.Xresources` `Xft.dpi`. +## Storage +Config data, including manga progress is stored under `$XDG_CONFIG_HOME/quickmedia` or `~/.config/quickmedia`.\ +Cache is stored under `$XDG_CACHE_HOME/quickmedia` or `~/.cache/quickmedia`. ## Tabs [tabbed](https://tools.suckless.org/tabbed/) can be used to put quickmedia windows into tabs. After installing `tabbed`, run `tabbed -c -k quickmedia launcher -e`. ## License @@ -12,7 +12,7 @@ Make code blocks on matrix and 4chan use monospace and have a background of a di Allow deleting watch history with delete key (and show confirmation). Add navigation to nyaa.si submitter torrents. Create a large texture and add downloaded images to it. This will save memory usage because sfml has to use power of two textures (and so does opengl internally) for textures, so if you have multiple textures they will use more memory than one large texture with the same texture data. -Use fallback cjk font for regular sf::Text as well (search, tabs, chapter name when viewing a page, path in file-manager, etc). That should also result in better performance on pinephone. +Use fallback cjk font for regular mgl::Text as well (search, tabs, chapter name when viewing a page, path in file-manager, etc). That should also result in better performance on pinephone. Fix some japanese fonts not rendering (half width alphanumeric?). Extract thumbnail from images that are being downloaded, while its downloading and show that while the full image is downloading (upscaled, or with blurhash). Add setting to disable sending typing events to the server (matrix). @@ -47,7 +47,7 @@ Add F5 to refresh page. Allow choosing which translation/scanlation to use on mangadex. Right now it uses the latest one, which is most likely to be the best. Retry download if it fails. Readd 4chan login page. -Modify sfml to use GL_COMPRESSED_LUMINANCE and other texture compression modes (in sf::Texture). This reduces memory usage by half. +Modify sfml to use GL_COMPRESSED_LUMINANCE and other texture compression modes (in mgl::Texture). This reduces memory usage by half. Decrease memory usage even further (mostly in matrix /sync when part of large rooms) by using rapidjson SAX style API to stream json string into SAX style parsing. Sometimes we fail to get images in mangadex, most common reason being that the manga is licensed and we can't view the manga on mangadex. QuickMedia should implement mangaplus and redirect us to mangaplus plugin to view the manga, or simply show that we cant view the manga because its licensed. Show redacted messages even when part of the initial sync in matrix. Right now they are hidden while new sync redacted messages are not. @@ -97,7 +97,6 @@ When fetching previous messages in matrix, fetching until there are 0 messages o QuickMedia crashes if you enter a room that you have left before. This could happen if cache is loaded that has a room that is then getting removed when sync is finished. Limit size of Entry and scroll content instead. Implement matrix spoiler, see: https://github.com/matrix-org/matrix-doc/blob/master/proposals/2010-spoilers.md. -Replace sfml font glyph loading completely with stb_freetype. Create a workaround for dwm terminal swallow patch stealing mpv when moving QuickMedia to another monitor sometimes. Maybe check for structure notify events on mpv and reparent and select input on the mpv window again? Add option to decline and mute user in invites. This is to combat invite spam, where muted users cant invite you. Add an option to select video resolution, if we want to use less power and less bandwidth for example. @@ -208,3 +207,9 @@ Support directly going to a youtube channel for a url. This is helpful for openi Support downloading soundcloud/youtube playlists. Such downloads should also have a different download gui as you would select a folder instead of an output file. Support downloading .m3u8 files, such as soundcloud music without using youtube-dl. Fix lbry and peertube download which fail because for lbry all videos are .m3u8 and some peertube videos are .m3u8. +Fix lbry and peertube download which fail because for lbry all videos are .m3u8 and some peertube videos are .m3u8. +Improve pinephone video load performance by not restarting mpv on next video. Instead start the video in the same video player. +Very large resolutions, such as 7680x2160 (id 272) for video https://www.youtube.com/watch?v=GxaH40zpvYc are not supported by the youtube android video loader. Find a way to fix that. +Use std::move(string) for all places where text.set_string is called. +Use reference for text.get_string() in all places. +Cleanup font sizes that are not visible (or not used for a while). Also do the same for character ranges font texture. diff --git a/external/stb/stb_image_write.h b/external/stb/stb_image_write.h new file mode 100644 index 0000000..023d71e --- /dev/null +++ b/external/stb/stb_image_write.h @@ -0,0 +1,1724 @@ +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + Andrew Kensler + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include <stdlib.h> + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBIW_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include <stdio.h> +#endif // STBI_WRITE_NO_STDIO + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include <assert.h> +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + stbiw__write_flush(s); + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef STBI_WRITE_NO_STDIO + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_LIB_EXT1__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<<bits[1])-1); +} + +static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { + const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] }; + const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] }; + int dataOff, i, j, n, diff, end0pos, x, y; + int DU[64]; + + // DCT rows + for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) { + stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]); + } + // DCT columns + for(dataOff=0; dataOff<8; ++dataOff) { + stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4], + &CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]); + } + // Quantize/descale/zigzag the coefficients + for(y = 0, j=0; y < 8; ++y) { + for(x = 0; x < 8; ++x,++j) { + float v; + i = y*du_stride+x; + v = CDU[i]*fdtbl[j]; + // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); + // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway? + DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); + } + } + + // Encode DC + diff = DU[0] - DC; + if (diff == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]); + } else { + unsigned short bits[2]; + stbiw__jpg_calcBits(diff, bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + // Encode ACs + end0pos = 63; + for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/
\ No newline at end of file diff --git a/generate-emoji-sprite.py b/generate-emoji-sprite.py index 887d6c8..bc26843 100755 --- a/generate-emoji-sprite.py +++ b/generate-emoji-sprite.py @@ -42,7 +42,6 @@ namespace QuickMedia { with open("generated/Emoji.cpp", "w") as source_file: source_file.write("""#include "Emoji.hpp" -#include <assert.h> // This file was automatically generated with generate-emoji-sprite.py, do not edit manually! @@ -66,8 +65,9 @@ namespace QuickMedia { } EmojiRectangle emoji_get_extents(uint32_t codepoint) { - assert(codepoint_is_emoji(codepoint)); + if(codepoint < 0x1F000 || codepoint - 0x1F000 >= %s) + return { 0, 0, 0, 0 }; return emoji_extents[codepoint - 0x1F000]; } } -""") +""" % len(emoji_characters)) diff --git a/generated/Emoji.cpp b/generated/Emoji.cpp index 999e909..c3b8b21 100644 --- a/generated/Emoji.cpp +++ b/generated/Emoji.cpp @@ -1,5 +1,4 @@ #include "Emoji.hpp" -#include <assert.h> // This file was automatically generated with generate-emoji-sprite.py, do not edit manually! @@ -2940,7 +2939,8 @@ namespace QuickMedia { } EmojiRectangle emoji_get_extents(uint32_t codepoint) { - assert(codepoint_is_emoji(codepoint)); + if(codepoint < 0x1F000 || codepoint - 0x1F000 >= 2928) + return { 0, 0, 0, 0 }; return emoji_extents[codepoint - 0x1F000]; } } diff --git a/images/arrow.png b/images/arrow.png Binary files differnew file mode 100644 index 0000000..d9a5887 --- /dev/null +++ b/images/arrow.png diff --git a/include/AsyncImageLoader.hpp b/include/AsyncImageLoader.hpp index 7556fbc..856e9cc 100644 --- a/include/AsyncImageLoader.hpp +++ b/include/AsyncImageLoader.hpp @@ -4,9 +4,10 @@ #include "../include/MessageQueue.hpp" #include "../include/FileAnalyzer.hpp" #include "../include/AsyncTask.hpp" -#include <SFML/System/Vector2.hpp> -#include <SFML/Graphics/Texture.hpp> -#include <SFML/System/Clock.hpp> +#include <mglpp/system/vec.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/graphics/Image.hpp> +#include <mglpp/system/Clock.hpp> #include <string> #include <memory> #include <unordered_map> @@ -22,8 +23,8 @@ namespace QuickMedia { struct ThumbnailData { LoadingState loading_state = LoadingState::NOT_LOADED; - sf::Texture texture; - std::unique_ptr<sf::Image> image; // Set in another thread. This should be .reset after loading it into |texture|, to save memory + mgl::Texture texture; + std::unique_ptr<mgl::Image> image; // Set in another thread. This should be .reset after loading it into |texture|, to save memory size_t counter = 0; Path thumbnail_path; }; @@ -33,12 +34,12 @@ namespace QuickMedia { Path thumbnail_path; bool local; std::shared_ptr<ThumbnailData> thumbnail_data; - sf::Vector2i resize_target_size; + mgl::vec2i resize_target_size; }; // If |symlink_if_no_resize| is false then a copy is made from |thumbnail_path| to |thumbnail_path_resized| instead of a symlink if |thumbnail_path| is not larger than |resize_target_size|. // One example of why you might not want a symlink is if |thumbnail_path| is a temporary file. - bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, sf::Vector2i resize_target_size, ContentType content_type, bool symlink_if_no_resize); + bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, mgl::vec2i resize_target_size, ContentType content_type, bool symlink_if_no_resize); constexpr int NUM_IMAGE_LOAD_PARALLEL = 4; @@ -50,7 +51,7 @@ namespace QuickMedia { // This function should be called every frame for the objects that need to display this thumbnail, otherwise it can be unloaded. // set |resize_target_size| to {0, 0} to disable resizing. // Note: this method is not thread-safe - std::shared_ptr<ThumbnailData> get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size); + std::shared_ptr<ThumbnailData> get_thumbnail(const std::string &url, bool local, mgl::vec2i resize_target_size); // Note: this should only be called once every frame. // Note: this method is not thread-safe @@ -61,7 +62,7 @@ namespace QuickMedia { int64_t download_start = 0; Path thumbnail_path; std::shared_ptr<ThumbnailData> thumbnail_data; - sf::Vector2i resize_target_size; + mgl::vec2i resize_target_size; std::string url; }; @@ -72,12 +73,12 @@ namespace QuickMedia { // set |resize_target_size| to {0, 0} to disable resizing. // Note: this method is not thread-safe - bool load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data, Path &thumbnail_path); + bool load_thumbnail(const std::string &url, bool local, mgl::vec2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data, Path &thumbnail_path); // Returns -1 if all threads are busy int get_free_load_index() const; - void load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size); + void load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, mgl::vec2i resize_target_size); void process_thumbnail(ThumbnailLoadData &thumbnail_load_data); private: void reset_download(Download &download); diff --git a/include/Body.hpp b/include/Body.hpp index d4eb49f..45749ce 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -2,11 +2,12 @@ #include "BodyItem.hpp" #include "RoundedRectangle.hpp" -#include <SFML/Graphics/RectangleShape.hpp> -#include <SFML/Graphics/Sprite.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/graphics/Sprite.hpp> +#include <functional> -namespace sf { - class RenderWindow; +namespace mgl { + class Window; class Shader; class Texture; class Event; @@ -17,6 +18,8 @@ namespace Json { } namespace QuickMedia { + void init_body_themes(); + class ThumbnailData; using BodyItemRenderCallback = std::function<void(std::shared_ptr<BodyItem> &body_item)>; // Return true to merge @@ -41,7 +44,7 @@ namespace QuickMedia { class Body { public: - Body(BodyTheme body_theme, sf::Texture &loading_icon_texture, sf::Shader *rounded_rectangle_shader, sf::Shader *rounded_rectangle_mask_shader); + Body(BodyTheme body_theme, mgl::Texture &loading_icon_texture, mgl::Shader *rounded_rectangle_shader, mgl::Shader *rounded_rectangle_mask_shader); ~Body(); // Select previous page, ignoring invisible items. Returns true if the item was changed. This can be used to check if the top was hit when wrap_around is set to false @@ -102,17 +105,17 @@ namespace QuickMedia { void clamp_selection(); // Returns true if the event was handled - bool on_event(const sf::RenderWindow &window, const sf::Event &event, bool keyboard_navigation = true); + bool on_event(const mgl::Window &window, const mgl::Event &event, bool keyboard_navigation = true); // Returns the the body total height - double draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size); + double draw(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size); // Returns the the body total height - double draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress); + double draw(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, const Json::Value &content_progress); // |size| is the clip size, another outside this will be cut off. // Note: this should be called after |draw|, or thumbnails will be messed up. TODO: find a way to solve this issue in a clean way. // This happens because of |draw| sets thumbnails as unreferenced at the beginning and cleans them up at the end if they are not drawn in the same function call. // TODO: Right now drawing an item that also exists in the body will cause the text to update geometry every frame if the text is wrapping text and the items are drawn at different sizes, // because of Text::setMaxWidth - void draw_item(sf::RenderWindow &window, std::shared_ptr<BodyItem> &item, sf::Vector2f pos, sf::Vector2f size, bool include_embedded_item = true, bool is_embedded = false); + void draw_item(mgl::Window &window, std::shared_ptr<BodyItem> &item, mgl::vec2f pos, mgl::vec2f size, bool include_embedded_item = true, bool is_embedded = false); float get_item_height(BodyItem *item, float width, bool load_texture = true, bool include_embedded_item = true, bool merge_with_previous = false, int item_index = -1); @@ -137,11 +140,11 @@ namespace QuickMedia { bool draw_thumbnails; // Set to {0, 0} to disable resizing - sf::Vector2i thumbnail_max_size; + mgl::vec2i thumbnail_max_size; BodyItemRenderCallback body_item_render_callback; BodyItemMergeHandler body_item_merge_handler; std::function<void(BodyItem*)> body_item_select_callback; - sf::Shader *thumbnail_mask_shader; + mgl::Shader *thumbnail_mask_shader; AttachSide attach_side = AttachSide::TOP; bool title_mark_urls = false; bool swiping_enabled = false; @@ -151,18 +154,18 @@ namespace QuickMedia { std::function<void()> on_top_reached = nullptr; std::function<void()> on_bottom_reached = nullptr; private: - void draw_drop_shadow(sf::RenderWindow &window); + void draw_drop_shadow(mgl::Window &window); void filter_search_fuzzy_item(const std::string &text, BodyItem *body_item); - void handle_item_render(const sf::Vector2f pos, const float item_width, const float item_height, int item_index); + void handle_item_render(const mgl::vec2f pos, const float item_width, const float item_height, int item_index); // Returns the the body total height - double draw_list_view(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress); + double draw_list_view(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, const Json::Value &content_progress); // Returns the the body total height - double draw_card_view(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, sf::Vector2u window_size, float scissor_y); - void draw_item(sf::RenderWindow &window, std::shared_ptr<BodyItem> &item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item = true, bool merge_with_previous = false); - void draw_card_item(sf::RenderWindow &window, std::shared_ptr<BodyItem> &item, const sf::Vector2f &pos, const sf::Vector2f &pos_offset, const sf::Vector2f &body_size, const sf::Vector2f &window_size, float item_height, float scissor_y, int item_index, ThumbnailData *item_thumbnail); + double draw_card_view(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::vec2i window_size, float scissor_y, bool &switched_to_list_view); + void draw_item(mgl::Window &window, std::shared_ptr<BodyItem> &item, const mgl::vec2f &pos, const mgl::vec2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item = true, bool merge_with_previous = false); + void draw_card_item(mgl::Window &window, std::shared_ptr<BodyItem> &item, const mgl::vec2f &pos, const mgl::vec2f &pos_offset, const mgl::vec2f &body_size, const mgl::vec2f &window_size, float item_height, float scissor_y, int item_index, ThumbnailData *item_thumbnail); void update_dirty_state(BodyItem *body_item, float width); void clear_body_item_cache(BodyItem *body_item); - sf::Vector2i get_item_thumbnail_size(BodyItem *item) const; + mgl::vec2i get_item_thumbnail_size(BodyItem *item) const; // Returns -1 if not found int get_previous_visible_item(int start_index); @@ -189,9 +192,9 @@ namespace QuickMedia { struct RenderItem { std::shared_ptr<BodyItem> body_item; - sf::Vector2f pos; - sf::Vector2f pos_offset; - sf::Vector2f size; + mgl::vec2f pos; + mgl::vec2f pos_offset; + mgl::vec2f size; float item_height; int item_index; bool merge_with_previous; @@ -212,14 +215,14 @@ namespace QuickMedia { float selected_scrolled = 0.0f; // TODO: Use a loading gif or something similar instead, to properly indicate the image is loading. Which could also have text that says "failed to load image" when image loading failed. // TODO: Use rounded rectangle instead - sf::RectangleShape image_fallback; - //sf::RectangleShape item_separator; - sf::Sprite image; - sf::Sprite loading_icon; + mgl::Rectangle image_fallback; + //mgl::Rectangle item_separator; + mgl::Sprite image; + mgl::Sprite loading_icon; - sf::Text progress_text; - sf::Text replies_text; - sf::Text embedded_item_load_text; + mgl::Text progress_text; + mgl::Text replies_text; + mgl::Text embedded_item_load_text; bool body_size_changed = false; @@ -228,8 +231,8 @@ namespace QuickMedia { bool bottom_cut_off; int first_visible_item = -1; int last_visible_item = -1; - sf::Clock draw_timer; - sf::Clock frame_timer; + mgl::Clock draw_timer; + mgl::Clock frame_timer; double elapsed_time_sec = 0.0; bool selected_line_top_visible = true; bool selected_line_bottom_visible = true; @@ -242,15 +245,15 @@ namespace QuickMedia { int selected_column = 0; bool card_view_enabled = true; StuckDirection stuck_direction = StuckDirection::NONE; - sf::Vector2f mouse_click_pos; - sf::Vector2f mouse_release_pos; + mgl::vec2f mouse_click_pos; + mgl::vec2f mouse_release_pos; double mouse_press_pixels_moved_abs; - sf::Vector2f mouse_pos; - sf::Vector2i prev_mouse_pos_raw; - sf::Vector2i mouse_pos_raw; - sf::Vector2f mouse_scroll_accel; - sf::Vector2f body_pos; - sf::Vector2f body_size; + mgl::vec2f mouse_pos; + mgl::vec2i prev_mouse_pos_raw; + mgl::vec2i mouse_pos_raw; + mgl::vec2f mouse_scroll_accel; + mgl::vec2f body_pos; + mgl::vec2f body_size; double body_swipe_x = 0.0; bool body_swipe_move_right = false; bool grabbed_left_side = false; @@ -259,15 +262,15 @@ namespace QuickMedia { RoundedRectangle item_background; RoundedRectangle reaction_background; bool render_selected_item_bg = true; - sf::Vector2f item_background_prev_pos; - sf::Vector2f item_background_target_pos; - sf::Vector2f item_background_prev_size; - sf::Vector2f item_background_target_size; + mgl::vec2f item_background_prev_pos; + mgl::vec2f item_background_target_pos; + mgl::vec2f item_background_prev_size; + mgl::vec2f item_background_target_size; float item_background_target_height = 0.0f; TargetSetState target_set = TargetSetState::NOT_SET; std::string current_filter; bool using_filter = false; - sf::Shader *rounded_rectangle_shader; - sf::Shader *rounded_rectangle_mask_shader; + mgl::Shader *rounded_rectangle_shader; + mgl::Shader *rounded_rectangle_mask_shader; }; }
\ No newline at end of file diff --git a/include/BodyItem.hpp b/include/BodyItem.hpp index 5889d58..eef674e 100644 --- a/include/BodyItem.hpp +++ b/include/BodyItem.hpp @@ -2,10 +2,10 @@ #include "Text.hpp" #include <memory> -#include <SFML/Graphics/Text.hpp> +#include <mglpp/graphics/Text.hpp> -namespace sf { - class RenderTarget; +namespace mgl { + class Window; } namespace QuickMedia { @@ -77,21 +77,21 @@ namespace QuickMedia { dirty_timestamp = true; } - void set_title_color(sf::Color new_color) { + void set_title_color(mgl::Color new_color) { if(new_color == title_color) return; title_color = new_color; dirty = true; } - void set_description_color(sf::Color new_color) { + void set_description_color(mgl::Color new_color) { if(new_color == description_color) return; description_color = new_color; dirty_description = true; } - void set_author_color(sf::Color new_color) { + void set_author_color(mgl::Color new_color) { if(new_color == author_color) return; author_color = new_color; @@ -117,13 +117,13 @@ namespace QuickMedia { // In milliseconds int64_t get_timestamp() const { return timestamp; } - sf::Color get_title_color() const { return title_color; } - sf::Color get_description_color() const { return description_color; } - sf::Color get_author_color() const { return author_color; } + mgl::Color get_title_color() const { return title_color; } + mgl::Color get_description_color() const { return description_color; } + mgl::Color get_author_color() const { return author_color; } bool is_selectable() const { return selectable; } - void draw_list(Body *body, sf::RenderTarget &render_target); + void draw_list(Body *body, mgl::Window &render_target); // TODO: Use a list of strings instead, not all plugins need all of these fields std::string url; @@ -138,7 +138,7 @@ namespace QuickMedia { std::unique_ptr<Text> title_text; std::unique_ptr<Text> description_text; std::unique_ptr<Text> author_text; - std::unique_ptr<sf::Text> timestamp_text; // TODO: Remove + std::unique_ptr<mgl::Text> timestamp_text; // TODO: Remove // Used by image boards for example. The elements are indices to other body items std::vector<size_t> replies_to; // Used by image boards for example. The elements are indices to other body items @@ -148,7 +148,7 @@ namespace QuickMedia { float loaded_height = 0.0f; float height = 0.0f; float prev_height = 0.0f; - sf::Vector2f loaded_image_size; + mgl::vec2f loaded_image_size; float loaded_content_height = 0.0f; FetchStatus embedded_item_status = FetchStatus::NONE; // Important! Should refer to a new BodyItem, not one that already exists in the body. @@ -156,7 +156,7 @@ namespace QuickMedia { // if an embedded item wraps but not the original body item. std::shared_ptr<BodyItem> embedded_item; // Used by matrix for example to display reply message body. Note: only the first level of embedded items is rendered (not recursive, this is done on purpose) ThumbnailMaskType thumbnail_mask_type = ThumbnailMaskType::NONE; - sf::Vector2i thumbnail_size; + mgl::vec2i thumbnail_size; std::vector<Reaction> reactions; // TODO: Move to a different body item type std::shared_ptr<BodyItemExtra> extra; // TODO: Remove @@ -171,9 +171,9 @@ namespace QuickMedia { std::string description; std::string author; int64_t timestamp; - sf::Color title_color; - sf::Color author_color; - sf::Color description_color; + mgl::Color title_color; + mgl::Color author_color; + mgl::Color description_color; bool selectable; }; diff --git a/include/Entry.hpp b/include/Entry.hpp index 1ee237b..00c22a4 100644 --- a/include/Entry.hpp +++ b/include/Entry.hpp @@ -2,13 +2,13 @@ #include "Text.hpp" #include "RoundedRectangle.hpp" -#include <SFML/Graphics/Text.hpp> +#include <mglpp/graphics/Text.hpp> #include <functional> -namespace sf { +namespace mgl { class Font; class Event; - class RenderWindow; + class Window; class Shader; } @@ -18,24 +18,24 @@ namespace QuickMedia { class Entry { public: - Entry(const std::string &placeholder_text, sf::Shader *rounded_rectangle_shader); - void process_event(sf::Event &event); - void draw(sf::RenderWindow &window); + Entry(const std::string &placeholder_text, mgl::Shader *rounded_rectangle_shader); + void process_event(mgl::Window &window, mgl::Event &event); + void draw(mgl::Window &window); void set_single_line(bool single_line); void set_editable(bool editable); - void set_text(const std::string &text); - void set_position(const sf::Vector2f &pos); + void set_text(std::string text); + void set_position(const mgl::vec2f &pos); void set_max_width(float width); void move_caret_to_end(); - void append_text(const std::string &str); + void insert_text_at_caret_position(const std::string &str); - void replace(size_t start_index, size_t length, const sf::String &insert_str); + void replace(size_t start_index, size_t length, const std::string &insert_str); int get_caret_index() const; bool is_editable() const; float get_height(); - const sf::String& get_text() const; + const std::string& get_text() const; OnEntrySubmit on_submit_callback; bool draw_background; @@ -43,7 +43,7 @@ namespace QuickMedia { Text text; float width; RoundedRectangle background; - sf::Text placeholder; + mgl::Text placeholder; bool mouse_left_inside; }; }
\ No newline at end of file diff --git a/include/ImageViewer.hpp b/include/ImageViewer.hpp index d503f82..182a6a2 100644 --- a/include/ImageViewer.hpp +++ b/include/ImageViewer.hpp @@ -3,16 +3,15 @@ #include "Path.hpp" #include <string> #include <vector> -#include <SFML/Graphics/Texture.hpp> -#include <SFML/Graphics/Sprite.hpp> -#include <SFML/Graphics/Text.hpp> -#include <SFML/System/Clock.hpp> -#include <SFML/Window/Cursor.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/graphics/Sprite.hpp> +#include <mglpp/graphics/Text.hpp> +#include <mglpp/system/Clock.hpp> #include <thread> #include <memory> -namespace sf { - class RenderWindow; +namespace mgl { + class Window; } namespace QuickMedia { @@ -25,17 +24,17 @@ namespace QuickMedia { }; struct ImageData { - sf::Texture texture; - sf::Sprite sprite; + mgl::Texture texture; + mgl::Sprite sprite; ImageStatus image_status; - std::unique_ptr<sf::Image> image; + std::unique_ptr<mgl::Image> image; bool visible_on_screen; float prev_height = 0.0f; }; struct PageSize { - sf::Vector2<double> size; - sf::Vector2<double> prev_size; + mgl::vec2d size; + mgl::vec2d prev_size; bool loaded; }; @@ -47,7 +46,7 @@ namespace QuickMedia { class ImageViewer { public: - ImageViewer(sf::RenderWindow *window, int num_pages, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir, bool *fit_image_to_window); + ImageViewer(mgl::Window *window, int num_pages, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir, bool *fit_image_to_window); ~ImageViewer(); ImageViewerAction draw(); // Returns page as 1 indexed @@ -55,10 +54,10 @@ namespace QuickMedia { int get_num_pages() const { return num_pages; } private: void load_image_async(const Path &path, std::shared_ptr<ImageData> image_data); - bool render_page(sf::RenderWindow &window, int page, double offset_y); - sf::Vector2<double> get_page_size(int page); + bool render_page(mgl::Window &window, int page, double offset_y); + mgl::vec2d get_page_size(int page); private: - sf::RenderWindow *window; + mgl::Window *window; int current_page; int num_pages; @@ -75,20 +74,20 @@ namespace QuickMedia { int focused_page; int prev_focused_page = -1; - sf::Font *font; - sf::Clock frame_timer; - sf::Text page_text; + mgl::Clock frame_timer; + mgl::Text page_text; std::vector<std::shared_ptr<ImageData>> image_data; std::vector<PageSize> page_size; - sf::Vector2<double> window_size; + mgl::vec2d window_size; bool window_size_set = false; bool middle_mouse_scrolling = false; double autoscroll_start_y = 0.0; - sf::Cursor default_cursor; - sf::Cursor size_vertical_cursor; + // TODO: Fix + //sf::Cursor default_cursor; + //sf::Cursor size_vertical_cursor; bool has_default_cursor; bool has_size_vertical_cursor; diff --git a/include/NetUtils.hpp b/include/NetUtils.hpp index a142884..f57f942 100644 --- a/include/NetUtils.hpp +++ b/include/NetUtils.hpp @@ -15,6 +15,5 @@ namespace QuickMedia { std::string url_param_decode(const std::string ¶m); std::vector<Range> extract_urls(const std::string &str); std::vector<std::string> ranges_get_strings(const std::string &str, const std::vector<Range> &ranges); - void convert_utf8_to_utf32_ranges(const std::string &str, std::vector<Range> &ranges); std::string header_extract_value(const std::string &header, const std::string &type); }
\ No newline at end of file diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index c19e1f6..d4166ec 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -9,16 +9,16 @@ #include "../plugins/FileManager.hpp" #include <vector> #include <memory> -#include <SFML/Graphics/Font.hpp> -#include <SFML/Graphics/RenderWindow.hpp> -#include <SFML/Graphics/Shader.hpp> +#include <mglpp/graphics/Font.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Shader.hpp> #include <unordered_set> #include <json/value.h> #include <future> #include <thread> #include <stack> #include <X11/Xlib.h> -#include <X11/Xatom.h> namespace QuickMedia { class Matrix; @@ -69,7 +69,7 @@ namespace QuickMedia { bool fetching_next_page_failed = false; bool search_suggestion_submitted = false; int fetched_page = 0; - sf::Text search_result_text; + mgl::Text search_result_text; AsyncTask<FetchResult> fetch_future; AsyncTask<BodyItems> next_page_future; }; @@ -100,22 +100,24 @@ namespace QuickMedia { void youtube_get_watch_history(BodyItems &history_items); Json::Value load_video_history_json(); + + void set_clipboard(const std::string &str); private: void init(Window parent_window, std::string &program_path); void load_plugin_by_name(std::vector<Tab> &tabs, int &start_tab_index, FileManagerMimeType fm_mime_type, FileSelectionHandler file_selection_handler, std::string instance); - void common_event_handler(sf::Event &event); + void common_event_handler(mgl::Event &event); void handle_x11_events(); - void base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_key_press = true, bool handle_searchbar = true); - void event_idle_handler(const sf::Event &event); + void base_event_handler(mgl::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_key_press = true, bool handle_searchbar = true); + void event_idle_handler(const mgl::Event &event); void idle_active_handler(); void update_idle_state(); bool show_info_page(BodyItem *body_item, bool include_reverse_image_search); bool toggle_bookmark(BodyItem *body_item, const char *bookmark_name); - void page_loop_render(sf::RenderWindow &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs); + void page_loop_render(mgl::Window &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs); using PageLoopSubmitHandler = std::function<void(const std::vector<Tab> &new_tabs)>; // Returns false if the page loop was escaped by user navigation (pressing escape) or if there was an error at startup bool page_loop(std::vector<Tab> &tabs, int start_tab_index = 0, PageLoopSubmitHandler after_submit_handler = nullptr, bool go_to_previous_on_escape = true); - void video_page_download_video(const std::string &url, sf::WindowHandle video_player_window = None); + void video_page_download_video(const std::string &url, mgl::WindowHandle video_player_window = None); bool video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page); int video_get_max_height(); void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = ""); @@ -137,7 +139,7 @@ namespace QuickMedia { DOWNLOAD_IN_PROGRESS }; - LoadImageResult load_image_by_index(int image_index, sf::Texture &image_texture, sf::String &error_message); + LoadImageResult load_image_by_index(int image_index, mgl::Texture &image_texture, std::string &error_message); void download_chapter_images_if_needed(MangaImagesPage *images_page); void select_episode(BodyItem *item, bool start_from_beginning); @@ -151,16 +153,16 @@ namespace QuickMedia { }; Display *disp; - sf::RenderWindow window; + mgl::Window window; Matrix *matrix = nullptr; bool is_login_sync = false; int monitor_hz; - sf::Vector2f window_size; + mgl::vec2i window_size; const char *plugin_name = nullptr; - sf::Texture plugin_logo; - sf::Texture loading_icon; - sf::Sprite load_sprite; - sf::Clock load_sprite_timer; + mgl::Texture plugin_logo; + mgl::Texture loading_icon; + mgl::Sprite load_sprite; + mgl::Clock load_sprite_timer; PageType current_page; std::stack<PageType> page_stack; int image_index; @@ -182,9 +184,9 @@ namespace QuickMedia { int num_manga_pages = 0; int exit_code = 0; std::string resources_root; - sf::Shader circle_mask_shader; - sf::Shader rounded_rectangle_shader; - sf::Shader rounded_rectangle_mask_shader; + mgl::Shader circle_mask_shader; + mgl::Shader rounded_rectangle_shader; + mgl::Shader rounded_rectangle_mask_shader; bool no_video = false; bool force_no_video = false; UpscaleImageAction upscale_image_action = UpscaleImageAction::NO; @@ -195,20 +197,16 @@ namespace QuickMedia { RoomData *current_chat_room = nullptr; bool go_to_previous_page = false; bool running_task_with_loading_screen = false; - sf::Text tab_text; - sf::Vertex gradient_points[4]; - sf::Vector2f body_pos; - sf::Vector2f body_size; + mgl::Vertex gradient_points[4]; + mgl::vec2f body_pos; + mgl::vec2f body_size; bool show_room_side_panel = true; std::thread::id main_thread_id; - sf::Clock idle_timer; + mgl::Clock idle_timer; bool idle = true; bool low_cpu_mode = false; bool window_closed = false; std::string pipe_selected_text; - Window x11_window = None; - Atom wm_delete_window_atom; - Atom net_wm_ping_atom; XEvent xev; std::filesystem::path file_manager_start_dir; std::string youtube_url; @@ -216,4 +214,4 @@ namespace QuickMedia { bool use_youtube_dl = false; int video_max_height = 0; }; -}
\ No newline at end of file +} diff --git a/include/ResourceLoader.hpp b/include/ResourceLoader.hpp index 52f4975..f2b2888 100644 --- a/include/ResourceLoader.hpp +++ b/include/ResourceLoader.hpp @@ -1,6 +1,6 @@ #pragma once -namespace sf { +namespace mgl { class Font; class Texture; } @@ -20,10 +20,10 @@ namespace QuickMedia::FontLoader { }; // Note: not thread-safe - sf::Font* get_font(FontType font_type); + mgl::Font* get_font(FontType font_type, unsigned int character_size); } namespace QuickMedia::TextureLoader { - // Note: not thread-safe - sf::Texture* get_texture(const char *filepath); + // Note: not thread-safe. Filepath is relative to the resource loader root path + mgl::Texture* get_texture(const char *filepath, bool pixel_coordinates = false); }
\ No newline at end of file diff --git a/include/RoundedRectangle.hpp b/include/RoundedRectangle.hpp index 96e57fd..dcf652c 100644 --- a/include/RoundedRectangle.hpp +++ b/include/RoundedRectangle.hpp @@ -1,28 +1,29 @@ #pragma once -#include <SFML/System/Vector2.hpp> -#include <SFML/Graphics/Vertex.hpp> +#include <mglpp/graphics/Color.hpp> +#include <mglpp/graphics/Vertex.hpp> +#include <mglpp/system/vec.hpp> -namespace sf { - class RenderTarget; +namespace mgl { + class Window; class Shader; } namespace QuickMedia { class RoundedRectangle { public: - RoundedRectangle(sf::Vector2f size, float radius, sf::Color color, sf::Shader *rounded_rectangle_shader); - void set_position(sf::Vector2f pos); - void set_size(sf::Vector2f size); - void set_color(sf::Color color); - sf::Vector2f get_position() const; - sf::Vector2f get_size() const; - void draw(sf::RenderTarget &target); + RoundedRectangle(mgl::vec2f size, float radius, mgl::Color color, mgl::Shader *rounded_rectangle_shader); + void set_position(mgl::vec2f pos); + void set_size(mgl::vec2f size); + void set_color(mgl::Color color); + mgl::vec2f get_position() const; + mgl::vec2f get_size() const; + void draw(mgl::Window &target); private: float radius; - sf::Vector2f pos; - sf::Vector2f size; - sf::Vertex vertices[4]; - sf::Shader *rounded_rectangle_shader; + mgl::vec2f pos; + mgl::vec2f size; + mgl::Vertex vertices[4]; + mgl::Shader *rounded_rectangle_shader; }; }
\ No newline at end of file diff --git a/include/Scale.hpp b/include/Scale.hpp index eb902cd..daf1e75 100644 --- a/include/Scale.hpp +++ b/include/Scale.hpp @@ -1,10 +1,10 @@ #pragma once -#include <SFML/System/Vector2.hpp> +#include <mglpp/system/vec.hpp> namespace QuickMedia { - template<typename T> - static T wrap_to_size_x(const T &size, const T &clamp_size) { + template<typename T, typename A> + static T wrap_to_size_x(const T &size, A clamp_size) { T new_size; if(size.x == 0) { new_size.x = 0; @@ -12,13 +12,13 @@ namespace QuickMedia { return new_size; } float size_ratio = (float)size.y / (float)size.x; - new_size.x = clamp_size.x; + new_size.x = clamp_size; new_size.y = new_size.x * size_ratio; return new_size; } - template<typename T> - static T wrap_to_size_y(const T &size, const T &clamp_size) { + template<typename T, typename A> + static T wrap_to_size_y(const T &size, A clamp_size) { T new_size; if(size.y == 0) { new_size.x = 0; @@ -26,7 +26,7 @@ namespace QuickMedia { return new_size; } float size_ratio = (float)size.x / (float)size.y; - new_size.y = clamp_size.y; + new_size.y = clamp_size; new_size.x = new_size.y * size_ratio; return new_size; } @@ -34,20 +34,28 @@ namespace QuickMedia { template<typename T> static T wrap_to_size(const T &size, const T &clamp_size) { T new_size; - new_size = wrap_to_size_x(size, clamp_size); + new_size = wrap_to_size_x(size, clamp_size.x); if(new_size.y > clamp_size.y) - new_size = wrap_to_size_y(size, clamp_size); + new_size = wrap_to_size_y(size, clamp_size.y); return new_size; } - template<typename T> - static T clamp_to_size_x(const T &size, const T &clamp_size) { + template<typename T, typename A> + static T clamp_to_size_x(const T &size, A clamp_size) { T new_size = size; - if(size.x > clamp_size.x) + if(size.x > clamp_size) new_size = wrap_to_size_x(new_size, clamp_size); return new_size; } + template<typename T, typename A> + static T clamp_to_size_y(const T &size, const A clamp_size) { + T new_size = size; + if(size.y > clamp_size) + new_size = wrap_to_size_y(new_size, clamp_size); + return new_size; + } + template<typename T> static T clamp_to_size(const T &size, const T &clamp_size) { T new_size = size; @@ -57,10 +65,10 @@ namespace QuickMedia { } template<typename T> - static sf::Vector2f get_ratio(const T &original_size, const T &new_size) { + static mgl::vec2f get_ratio(const T &original_size, const T &new_size) { if(original_size.x == 0 || original_size.y == 0) - return sf::Vector2f(0.0f, 0.0f); + return mgl::vec2f(0.0f, 0.0f); else - return sf::Vector2f((float)new_size.x / (float)original_size.x, (float)new_size.y / (float)original_size.y); + return mgl::vec2f((float)new_size.x / (float)original_size.x, (float)new_size.y / (float)original_size.y); } }
\ No newline at end of file diff --git a/include/SearchBar.hpp b/include/SearchBar.hpp index 12daf58..9170f7a 100644 --- a/include/SearchBar.hpp +++ b/include/SearchBar.hpp @@ -1,16 +1,17 @@ #pragma once #include "RoundedRectangle.hpp" -#include <SFML/Graphics/Text.hpp> -#include <SFML/Graphics/RectangleShape.hpp> -#include <SFML/Graphics/Sprite.hpp> -#include <SFML/System/Clock.hpp> +#include <mglpp/window/Event.hpp> +#include <mglpp/graphics/Text.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/graphics/Sprite.hpp> +#include <mglpp/system/Clock.hpp> #include <functional> +#include <string> -namespace sf { +namespace mgl { class Font; - class RenderWindow; - class Event; + class Window; class Shader; } @@ -21,15 +22,15 @@ namespace QuickMedia { class SearchBar { public: - SearchBar(sf::Texture *plugin_logo, sf::Shader *rounded_rectangle_shader, const std::string &placeholder, bool input_masked = false); - void draw(sf::RenderWindow &window, sf::Vector2f size, bool draw_background); - void on_event(sf::Event &event); + SearchBar(mgl::Texture *plugin_logo, mgl::Shader *rounded_rectangle_shader, const std::string &placeholder, bool input_masked = false); + void draw(mgl::Window &window, mgl::vec2f size, bool draw_background); + void on_event(mgl::Window &window, mgl::Event &event); void update(); - void onWindowResize(const sf::Vector2f &window_size); + void onWindowResize(const mgl::vec2f &window_size); void clear(); void set_text(const std::string &text, bool update_search = true); void append_text(const std::string &text_to_add); - void set_position(sf::Vector2f pos); + void set_position(mgl::vec2f pos); void set_editable(bool editable); bool is_editable() const; @@ -42,20 +43,20 @@ namespace QuickMedia { TextUpdateCallback onTextUpdateCallback; TextSubmitCallback onTextSubmitCallback; TextBeginTypingCallback onTextBeginTypingCallback; - int text_autosearch_delay; + int text_autosearch_delay_ms; bool caret_visible; float padding_top = 0.0f; float padding_bottom = 0.0f; float padding_x = 10.0f; private: - void onTextEntered(sf::Uint32 codepoint); + void onTextEntered(const mgl::Event::TextEvent &text_event); private: - sf::Text text; + mgl::Text text; RoundedRectangle background; - sf::RectangleShape shade; - sf::RectangleShape caret; - sf::Sprite plugin_logo_sprite; + mgl::Rectangle shade; + mgl::Rectangle caret; + mgl::Sprite plugin_logo_sprite; std::string placeholder_str; bool show_placeholder; bool updated_search; @@ -65,9 +66,9 @@ namespace QuickMedia { bool typing; bool backspace_pressed; bool mouse_left_inside; - sf::Vector2f pos; - sf::Clock time_since_search_update; - sf::Vector2f prev_size; + mgl::vec2f pos; + mgl::Clock time_since_search_update; + mgl::vec2f prev_size; bool editable = true; }; }
\ No newline at end of file diff --git a/include/SfmlFixes.hpp b/include/SfmlFixes.hpp deleted file mode 100644 index fc893c5..0000000 --- a/include/SfmlFixes.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include <SFML/Graphics/Image.hpp> - -namespace QuickMedia { - // See: https://github.com/SFML/SFML/issues/1711 - // TODO: Remove when above is fixed, or fix ourselves and include the fixed sfml version as a dependency, - // or write our own image class (maybe even use stb like sfml does but no error reason printing) - bool load_image_from_file(sf::Image &image, const std::string &filepath); - bool load_image_from_memory(sf::Image &image, const void *data, size_t size); -}
\ No newline at end of file diff --git a/include/Tabs.hpp b/include/Tabs.hpp index 51a7e9d..a5ca8ab 100644 --- a/include/Tabs.hpp +++ b/include/Tabs.hpp @@ -1,14 +1,15 @@ #pragma once #include "RoundedRectangle.hpp" -#include <SFML/Graphics/RectangleShape.hpp> -#include <SFML/Graphics/Text.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/graphics/Text.hpp> +#include <mglpp/graphics/Sprite.hpp> #include <vector> #include <functional> -namespace sf { +namespace mgl { class Event; - class RenderWindow; + class Window; class Shader; } @@ -17,16 +18,16 @@ namespace QuickMedia { class Tabs { public: - Tabs(sf::Shader *rounded_rectangle_shader); - Tabs(sf::Shader *rounded_rectangle_shader, sf::Color shade_color); + Tabs(mgl::Shader *rounded_rectangle_shader); + Tabs(mgl::Shader *rounded_rectangle_shader, mgl::Color shade_color); static float get_height(); static float get_shade_height(); // Returns the id (index) of the tab. The ids start from 0 - int add_tab(const std::string &title, Body *body); - void on_event(sf::Event &event); - void draw(sf::RenderWindow &window, sf::Vector2f pos, float width); + int add_tab(std::string title, Body *body); + void on_event(mgl::Event &event); + void draw(mgl::Window &window, mgl::vec2f pos, float width); void set_text(int index, const std::string &text); @@ -39,19 +40,19 @@ namespace QuickMedia { float tab_index_to_x_offset(int index); private: struct Tab { - sf::Text text; - std::string label_utf8; // TODO: Remove + mgl::Text text; Body *body; }; std::vector<Tab> tabs; RoundedRectangle background; - sf::RectangleShape shade; + mgl::Rectangle shade; int selected_tab = 0; float scroll = 0.0f; float width_per_tab = 0.0f; float tab_background_width = 0.0f; float container_width = 0.0f; int tab_offset = 0; - sf::Color shade_color; + mgl::Color shade_color; + mgl::Sprite arrow_sprite; }; }
\ No newline at end of file diff --git a/include/Text.hpp b/include/Text.hpp index 8eb5c96..dc3a0f7 100644 --- a/include/Text.hpp +++ b/include/Text.hpp @@ -1,37 +1,26 @@ #pragma once #include "NetUtils.hpp" -#include <SFML/Graphics/VertexArray.hpp> -#include <SFML/Graphics/VertexBuffer.hpp> -#include <SFML/System/String.hpp> -#include <SFML/System/Clock.hpp> +#include <mglpp/system/FloatRect.hpp> +#include <mglpp/system/vec.hpp> +#include <mglpp/graphics/Color.hpp> +#include <mglpp/graphics/VertexBuffer.hpp> +#include <mglpp/graphics/Vertex.hpp> +#include <mglpp/system/Clock.hpp> #include <vector> +#include <string_view> +#include <array> #include "types.hpp" #include <assert.h> -namespace sf { +namespace mgl { class Font; class Event; - class RenderTarget; + class Window; } namespace QuickMedia { - struct StringViewUtf32 { - const u32 *data; - size_t size; - - StringViewUtf32() : data(nullptr), size(0) {} - StringViewUtf32(const u32 *data, usize size) : data(data), size(size) {} - - size_t find(const StringViewUtf32 &other, size_t offset = 0) const; - - u32 operator [] (usize index) const { - assert(index < size); - return data[index]; - } - }; - struct TextElement { enum class Type @@ -47,10 +36,9 @@ namespace QuickMedia }; TextElement() {} - TextElement(const StringViewUtf32 &_text, Type _type) : text(_text), type(_type), text_type(TextType::LATIN) {} + TextElement(std::string_view text, Type type) : text(text), type(type), text_type(TextType::LATIN) {} - StringViewUtf32 text; - sf::Vector2f position; + std::string_view text; Type type; //bool ownLine; // Currently only used for emoji, to make emoji bigger when it's the only thing on a line TextType text_type; @@ -60,33 +48,34 @@ namespace QuickMedia int vertices_index; // index to |vertices| VertexArray int index; // index within vertices[vertices_index] int line; - sf::Uint32 codepoint; + uint32_t codepoint; }; class Text { public: - Text(sf::String str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls = false); + Text(std::string str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls = false); - void setString(const sf::String &str); - const sf::String& getString() const; - void appendText(const sf::String &str); + void setString(std::string str); + const std::string& getString() const; + void appendText(const std::string &str); + void insert_text_at_caret_position(const std::string &str); - void setPosition(float x, float y); - void setPosition(const sf::Vector2f &position); - sf::Vector2f getPosition() const; + void set_position(float x, float y); + void set_position(const mgl::vec2f &position); + mgl::vec2f get_position() const; void setMaxWidth(float maxWidth); float getMaxWidth() const; void setCharacterSize(unsigned int characterSize); - unsigned int getCharacterSize() const; + unsigned int get_character_size() const; - void replace(size_t start_index, size_t length, const sf::String &insert_str); + void replace(size_t start_index, size_t length, const std::string &insert_str); int getCaretIndex() const; - void setFillColor(sf::Color color); + void set_color(mgl::Color color); void setLineSpacing(float lineSpacing); void setCharacterSpacing(float characterSpacing); void setEditable(bool editable); @@ -100,11 +89,11 @@ namespace QuickMedia // Note: won't update until @draw is called float getHeight() const; - void processEvent(const sf::Event &event); + void processEvent(mgl::Window &window, const mgl::Event &event); // Performs culling. @updateGeometry is called even if text is not visible if text is dirty, because updateGeometry might change the dimension of the text and make is visible. // Returns true if text was drawn on screen (if text is within window borders) - bool draw(sf::RenderTarget &target); + bool draw(mgl::Window &target); void updateGeometry(bool update_even_if_not_dirty = false); @@ -134,7 +123,7 @@ namespace QuickMedia void splitTextByFont(); - float font_get_real_height(sf::Font *font); + float font_get_real_height(mgl::Font *font); float get_text_quad_left_side(const VertexRef &vertex_ref) const; float get_text_quad_right_side(const VertexRef &vertex_ref) const; // If the index is past the end, then the caret offset is the right side of the last character, rather than the left side @@ -142,23 +131,24 @@ namespace QuickMedia VertexRef& get_vertex_ref_clamp(int index); // Takes into consideration if index is the last vertex and the last vertex is a newline, then it should be on its own line int get_vertex_line(int index) const; - sf::Uint32 get_vertex_codepoint(int index) const; + uint32_t get_vertex_codepoint(int index) const; + size_t get_string_index_from_caret_index(size_t caret_index) const; private: - sf::String str; // TODO: Remove this for non-editable text??? also replace with std::string? then we get more efficient editing of text + std::string str; // TODO: Remove this for non-editable text??? also replace with std::string? then we get more efficient editing of text const bool bold_font; unsigned int characterSize; - std::array<sf::VertexArray, 4> vertices; - std::array<sf::VertexBuffer, 4> vertex_buffers; + std::array<std::vector<mgl::Vertex>, 4> vertices; + std::array<mgl::VertexBuffer, 4> vertex_buffers; float maxWidth; - sf::Vector2f position; - sf::Color color; + mgl::vec2f position; + mgl::Color color; bool dirty; bool dirtyText; bool dirtyCaret; bool editable; bool highlight_urls; CaretMoveDirection caretMoveDirection; - sf::FloatRect boundingBox; + mgl::FloatRect boundingBox; int num_lines; float lineSpacing; float characterSpacing; @@ -166,8 +156,8 @@ namespace QuickMedia int caretIndex; float caret_offset_x; - sf::Vector2f caretPosition; - sf::Vector2u renderTargetSize; + mgl::vec2f caretPosition; + mgl::vec2u renderTarget_size; std::vector<VertexRef> vertices_linear; // TODO: Use textElements instead diff --git a/include/Theme.hpp b/include/Theme.hpp index 8f62040..974f4f7 100644 --- a/include/Theme.hpp +++ b/include/Theme.hpp @@ -1,6 +1,6 @@ #pragma once -#include <SFML/Graphics/Color.hpp> +#include <mglpp/graphics/Color.hpp> namespace QuickMedia { struct Theme { @@ -8,28 +8,28 @@ namespace QuickMedia { Theme(const Theme&) = delete; Theme&operator=(const Theme&) = delete; - sf::Color background_color = sf::Color(18, 21, 26); - sf::Color text_color = sf::Color(255, 255, 255); - sf::Color faded_text_color = sf::Color(255, 255, 255, 179); - sf::Color shade_color = sf::Color(28, 32, 39); - sf::Color selected_color = sf::Color(55, 60, 68); - sf::Color card_item_background_color = sf::Color(28, 32, 39); - sf::Color replies_text_color = sf::Color(129, 162, 190); - sf::Color placeholder_text_color = sf::Color(255, 255, 255, 100); - sf::Color image_loading_background_color = sf::Color(52, 58, 70); - sf::Color attention_alert_text_color = sf::Color(255, 100, 100); - sf::Color cancel_button_background_color = sf::Color(41, 45, 50); - sf::Color confirm_button_background_color = sf::Color(31, 117, 255); - sf::Color loading_bar_color = sf::Color(31, 117, 255); - sf::Color embedded_item_border_color = sf::Color(255, 255, 255); - sf::Color provisional_message_color = sf::Color(255, 255, 255, 150); - sf::Color failed_text_color = sf::Color(255, 0, 0); - sf::Color timestamp_text_color = sf::Color(185, 190, 198, 100); - sf::Color new_items_alert_color = sf::Color(128, 50, 50); - sf::Color arrow_color = sf::Color(255, 255, 255, 175); - sf::Color url_text_color = sf::Color(35, 140, 245); - sf::Color loading_page_color = sf::Color(175, 180, 188); - sf::Color more_items_color = sf::Color(150, 175, 255, 100); + mgl::Color background_color = mgl::Color(18, 21, 26); + mgl::Color text_color = mgl::Color(255, 255, 255); + mgl::Color faded_text_color = mgl::Color(255, 255, 255, 179); + mgl::Color shade_color = mgl::Color(28, 32, 39); + mgl::Color selected_color = mgl::Color(55, 60, 68); + mgl::Color card_item_background_color = mgl::Color(28, 32, 39); + mgl::Color replies_text_color = mgl::Color(129, 162, 190); + mgl::Color placeholder_text_color = mgl::Color(255, 255, 255, 100); + mgl::Color image_loading_background_color = mgl::Color(52, 58, 70); + mgl::Color attention_alert_text_color = mgl::Color(255, 100, 100); + mgl::Color cancel_button_background_color = mgl::Color(41, 45, 50); + mgl::Color confirm_button_background_color = mgl::Color(31, 117, 255); + mgl::Color loading_bar_color = mgl::Color(31, 117, 255); + mgl::Color embedded_item_border_color = mgl::Color(255, 255, 255); + mgl::Color provisional_message_color = mgl::Color(255, 255, 255, 150); + mgl::Color failed_text_color = mgl::Color(255, 0, 0); + mgl::Color timestamp_text_color = mgl::Color(185, 190, 198, 100); + mgl::Color new_items_alert_color = mgl::Color(128, 50, 50); + mgl::Color arrow_color = mgl::Color(255, 255, 255, 175); + mgl::Color url_text_color = mgl::Color(35, 140, 245); + mgl::Color loading_page_color = mgl::Color(175, 180, 188); + mgl::Color more_items_color = mgl::Color(150, 175, 255, 100); bool drop_shadow = false; }; diff --git a/include/Utils.hpp b/include/Utils.hpp index 138d47e..fe0e583 100644 --- a/include/Utils.hpp +++ b/include/Utils.hpp @@ -2,7 +2,7 @@ #include <time.h> #include <string> -#include <SFML/System/Vector2.hpp> +#include <mglpp/system/vec.hpp> namespace QuickMedia { void show_virtual_keyboard(); @@ -13,5 +13,5 @@ namespace QuickMedia { std::string unix_time_to_local_time_str(time_t unix_time); int64_t get_boottime_milliseconds(); - sf::Vector2f vec2f_floor(float x, float y); + mgl::vec2f vec2f_floor(float x, float y); }
\ No newline at end of file diff --git a/include/VideoPlayer.hpp b/include/VideoPlayer.hpp index 4eab812..2ccd280 100644 --- a/include/VideoPlayer.hpp +++ b/include/VideoPlayer.hpp @@ -1,8 +1,8 @@ #pragma once #include "MediaChapter.hpp" -#include <SFML/Window/WindowHandle.hpp> -#include <SFML/System/Clock.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/system/Clock.hpp> #include <functional> #include <json/value.h> #include <sys/un.h> @@ -10,7 +10,7 @@ namespace QuickMedia { using EventCallbackFunc = std::function<void(const char *event_name)>; - using VideoPlayerWindowCreateCallback = std::function<void(sf::WindowHandle window)>; + using VideoPlayerWindowCreateCallback = std::function<void(mgl::WindowHandle window)>; // Currently this video player launches mpv and embeds it into the QuickMedia window class VideoPlayer { @@ -41,7 +41,7 @@ namespace QuickMedia { VideoPlayer& operator=(const VideoPlayer&) = delete; // |audio_path| is only set when video and audio are separate files/urls. - Error load_video(const char *path, const char *audio_path, sf::WindowHandle parent_window, bool use_youtube_dl, const std::string &title, const std::string &start_time = "", const std::vector<MediaChapter> &chapters = {}); + Error load_video(const char *path, const char *audio_path, mgl::WindowHandle parent_window, bool use_youtube_dl, const std::string &title, const std::string &start_time = "", const std::vector<MediaChapter> &chapters = {}); // Should be called every update frame Error update(); @@ -56,7 +56,7 @@ namespace QuickMedia { int exit_status; private: Error send_command(const char *cmd, size_t size); - Error launch_video_process(const char *path, const char *audio_path, sf::WindowHandle parent_window, const std::string &title, const std::string &start_time); + Error launch_video_process(const char *path, const char *audio_path, mgl::WindowHandle parent_window, const std::string &title, const std::string &start_time); VideoPlayer::Error read_ipc_func(); private: std::string plugin_name; @@ -66,7 +66,7 @@ namespace QuickMedia { bool use_youtube_dl; pid_t video_process_id; bool connected_to_ipc; - sf::Clock retry_timer; + mgl::Clock retry_timer; int connect_tries; int find_window_tries; int monitor_height; @@ -75,8 +75,8 @@ namespace QuickMedia { char ipc_server_path[L_tmpnam]; EventCallbackFunc event_callback; VideoPlayerWindowCreateCallback window_create_callback; - sf::WindowHandle window_handle; - sf::WindowHandle parent_window; + mgl::WindowHandle window_handle; + mgl::WindowHandle parent_window; Display *display; unsigned int request_id; unsigned int expected_request_id; diff --git a/include/gui/Button.hpp b/include/gui/Button.hpp index ba6997b..2a22a88 100644 --- a/include/gui/Button.hpp +++ b/include/gui/Button.hpp @@ -2,12 +2,12 @@ #include "../RoundedRectangle.hpp" #include <string> -#include <SFML/Graphics/Text.hpp> +#include <mglpp/graphics/Text.hpp> -namespace sf { +namespace mgl { class Event; class Font; - class RenderTarget; + class Window; class Shader; } @@ -19,21 +19,21 @@ namespace QuickMedia { class Button { public: - Button(const std::string &label, sf::Font *font, unsigned int character_size, float width, sf::Shader *rounded_rectangle_shader, float scale = 1.0f); + Button(const std::string &label, mgl::Font *font, float width, mgl::Shader *rounded_rectangle_shader, float scale = 1.0f); - ButtonEvent on_event(sf::Event &event); - void draw(sf::RenderTarget &target); + ButtonEvent on_event(mgl::Event &event); + void draw(mgl::Window &target); - void set_background_color(sf::Color color); - void set_position(sf::Vector2f pos); + void set_background_color(mgl::Color color); + void set_position(mgl::vec2f pos); - sf::Vector2f get_position() const; + mgl::vec2f get_position() const; float get_width() const; - float get_height() const; + float get_height(); private: - sf::Text label; + mgl::Text label; RoundedRectangle background; - sf::Color background_color; + mgl::Color background_color; float scale; bool clicked_inside = false; }; diff --git a/plugins/ImageBoard.hpp b/plugins/ImageBoard.hpp index e7c4c5d..b6d84a1 100644 --- a/plugins/ImageBoard.hpp +++ b/plugins/ImageBoard.hpp @@ -29,7 +29,7 @@ namespace QuickMedia { const char* get_title() const override { return ""; } PageTypez get_type() const override { return PageTypez::IMAGE_BOARD_THREAD; } - void copy_to_clipboard(const BodyItem *body_item) const override; + void copy_to_clipboard(const BodyItem *body_item) override; bool autoplay_next_item() override { return true; } virtual PluginResult login(const std::string &token, const std::string &pin, std::string &response_msg); diff --git a/plugins/Info.hpp b/plugins/Info.hpp index fbf7a41..cdaefd3 100644 --- a/plugins/Info.hpp +++ b/plugins/Info.hpp @@ -10,7 +10,7 @@ namespace QuickMedia { PluginResult submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) override; bool submit_is_async() const override { return false; } - void copy_to_clipboard(const BodyItem *body_item) const override; + void copy_to_clipboard(const BodyItem *body_item) override; static std::shared_ptr<BodyItem> add_url(const std::string &url); static std::shared_ptr<BodyItem> add_reverse_image_search(const std::string &image_url); diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 680b8b6..0c96114 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -4,9 +4,10 @@ #include "../include/MessageQueue.hpp" #include "Plugin.hpp" #include "Page.hpp" -#include <SFML/Graphics/Color.hpp> +#include <mglpp/graphics/Color.hpp> #include <unordered_map> #include <unordered_set> +#include <map> #include <set> #include <mutex> #include <atomic> @@ -22,7 +23,7 @@ namespace QuickMedia { std::string remove_reply_formatting(const std::string &str); std::string message_get_body_remove_formatting(Message *message); std::string extract_first_line_remove_newline_elipses(const std::string &str, size_t max_length); - sf::Color user_id_to_color(const std::string &user_id); + mgl::Color user_id_to_color(const std::string &user_id); struct UserInfo { friend struct RoomData; @@ -30,7 +31,7 @@ namespace QuickMedia { UserInfo(RoomData *room, std::string user_id, std::string display_name, std::string avatar_url); RoomData *room; - const sf::Color display_name_color; + const mgl::Color display_name_color; const std::string user_id; private: std::string display_name; @@ -82,7 +83,7 @@ namespace QuickMedia { std::string url; std::string thumbnail_url; std::string related_event_id; - sf::Vector2i thumbnail_size; // Set to {0, 0} if not specified + mgl::vec2i thumbnail_size; // Set to {0, 0} if not specified RelatedEventType related_event_type = RelatedEventType::NONE; bool notification_mentions_me = false; bool cache = false; diff --git a/plugins/MediaGeneric.hpp b/plugins/MediaGeneric.hpp index 61f1ad9..f0d5462 100644 --- a/plugins/MediaGeneric.hpp +++ b/plugins/MediaGeneric.hpp @@ -34,7 +34,7 @@ namespace QuickMedia { class MediaGenericSearchPage : public Page { public: - MediaGenericSearchPage(Program *program, const char *website_url, sf::Vector2i thumbnail_max_size, bool cloudflare_bypass, std::vector<CommandArg> extra_commands = {}); + MediaGenericSearchPage(Program *program, const char *website_url, mgl::vec2i thumbnail_max_size, bool cloudflare_bypass, std::vector<CommandArg> extra_commands = {}); const char* get_title() const override { return "Search"; } bool search_is_filter() override { return false; } SearchResult search(const std::string &str, BodyItems &result_items) override; @@ -60,7 +60,7 @@ namespace QuickMedia { MediaGenericSearchPage& related_media_custom_handler(MediaRelatedCustomHandler handler); private: std::string website_url; - sf::Vector2i thumbnail_max_size; + mgl::vec2i thumbnail_max_size; MediaSearchQuery search_query; std::vector<MediaTextQuery> text_queries; std::vector<MediaThumbnailQuery> thumbnail_queries; diff --git a/plugins/Page.hpp b/plugins/Page.hpp index c07679d..b9f220d 100644 --- a/plugins/Page.hpp +++ b/plugins/Page.hpp @@ -74,12 +74,14 @@ namespace QuickMedia { virtual void on_navigate_to_page(Body *body) { (void)body; } virtual void cancel_operation() {} - virtual void copy_to_clipboard(const BodyItem *body_item) const; + virtual void copy_to_clipboard(const BodyItem *body_item); std::unique_ptr<Body> create_body(bool plain_text_list = false, bool prefer_card_view = false); std::unique_ptr<SearchBar> create_search_bar(const std::string &placeholder_text, int search_delay); bool load_manga_content_storage(const char *service_name, const std::string &manga_title, const std::string &manga_url, const std::string &manga_id); + + void set_clipboard(const std::string &str); Program *program; std::shared_ptr<BodyItem> submit_body_item; // TODO: Remove this diff --git a/plugins/Youtube.hpp b/plugins/Youtube.hpp index d3a57a6..2cfe36e 100644 --- a/plugins/Youtube.hpp +++ b/plugins/Youtube.hpp @@ -3,6 +3,7 @@ #include "Page.hpp" #include "../include/AsyncTask.hpp" #include <unordered_set> +#include <map> namespace QuickMedia { struct YoutubeFormat { diff --git a/project.conf b/project.conf index acfd065..fbeaa67 100644 --- a/project.conf +++ b/project.conf @@ -12,6 +12,5 @@ platforms = ["posix"] version = "c++17" [dependencies] -sfml-graphics = "2" x11 = "1" xrandr = "1" diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 4661960..c1d4aab 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -3,7 +3,6 @@ #include "../include/Program.hpp" #include "../include/ImageUtils.hpp" #include "../include/Scale.hpp" -#include "../include/SfmlFixes.hpp" #include "../include/Utils.hpp" #include "../external/hash-library/sha256.h" @@ -22,13 +21,16 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "../external/stb/stb_image_resize.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "../external/stb/stb_image_write.h" + namespace QuickMedia { static bool webp_to_png(const Path &thumbnail_path, const Path &destination_path) { const char *args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", thumbnail_path.data.c_str(), "--", destination_path.data.c_str(), nullptr}; return exec_program(args, nullptr, nullptr) == 0; } - bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, sf::Vector2i resize_target_size, ContentType content_type, bool symlink_if_no_resize) { + bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, mgl::vec2i resize_target_size, ContentType content_type, bool symlink_if_no_resize) { Path input_path = thumbnail_path; if(content_type == ContentType::IMAGE_WEBP) { @@ -56,8 +58,8 @@ namespace QuickMedia { if(getppid() != parent_pid) _exit(127); - sf::Image image; - if(!image.loadFromFile(input_path.data) || image.getSize().x == 0 || image.getSize().y == 0) { + mgl::Image image; + if(!image.load_from_file(input_path.data.c_str()) || image.get_size().x == 0 || image.get_size().y == 0) { fprintf(stderr, "Failed to load %s\n", input_path.data.c_str()); _exit(1); } @@ -65,7 +67,7 @@ namespace QuickMedia { Path result_path_tmp = thumbnail_path_resized; result_path_tmp.append(".tmp.png"); - if(image.getSize().x <= (unsigned int)resize_target_size.x && image.getSize().y <= (unsigned int)resize_target_size.y) { + if(image.get_size().x <= resize_target_size.x && image.get_size().y <= resize_target_size.y) { if(content_type == ContentType::IMAGE_WEBP) { if(rename_atomic(input_path.data.c_str(), thumbnail_path_resized.data.c_str()) == 0) _exit(0); @@ -109,17 +111,24 @@ namespace QuickMedia { close(output_file); } } else { - sf::Vector2u clamped_size = clamp_to_size(image.getSize(), sf::Vector2u(resize_target_size.x, resize_target_size.y)); - unsigned char *output_pixels = new unsigned char[clamped_size.x * clamped_size.y * 4]; - stbir_resize_uint8(image.getPixelsPtr(), image.getSize().x, image.getSize().y, 0, output_pixels, clamped_size.x, clamped_size.y, 0, 4); - - // TODO: Remove this and use stb write to remove this unecessary extra copy of the data and write the data directly to file after converting it to png - sf::Image destination_image; - destination_image.create(clamped_size.x, clamped_size.y, output_pixels); - if(!destination_image.saveToFile(result_path_tmp.data)) { - fprintf(stderr, "Failed to save %s\n", thumbnail_path_resized.data.c_str()); - _exit(1); + mgl::vec2i clamped_size = clamp_to_size(image.get_size(), mgl::vec2i(resize_target_size.x, resize_target_size.y)); + unsigned char *output_pixels = new unsigned char[clamped_size.x * clamped_size.y * image.get_num_channels()]; + stbir_resize_uint8(image.data(), image.get_size().x, image.get_size().y, 0, output_pixels, clamped_size.x, clamped_size.y, 0, image.get_num_channels()); + + if(image.get_num_channels() == 4) { + if(!stbi_write_png(result_path_tmp.data.c_str(), clamped_size.x, clamped_size.y, 4, output_pixels, 0)) { + fprintf(stderr, "Failed to save %s\n", thumbnail_path_resized.data.c_str()); + _exit(1); + } + } else { + if(!stbi_write_jpg(result_path_tmp.data.c_str(), clamped_size.x, clamped_size.y, image.get_num_channels(), output_pixels, 0)) { + fprintf(stderr, "Failed to save %s\n", thumbnail_path_resized.data.c_str()); + _exit(1); + } } + + // TODO: Resize and save to dxt format which can be loaded faster, uses less memory during loading (since no conversion is needed to upload to gpu), + // it uses less memory while in gpu (because its in dxt compressed format) and it gives better rendering performance because of compressed image = smaller = better cache utilization. } if(rename_atomic(result_path_tmp.data.c_str(), thumbnail_path_resized.data.c_str()) == 0) @@ -147,11 +156,11 @@ namespace QuickMedia { return true; } - void AsyncImageLoader::load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) { + void AsyncImageLoader::load_create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, mgl::vec2i resize_target_size) { FileAnalyzer file_analyzer; if(!file_analyzer.load_file(thumbnail_path.data.c_str(), false)) { fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str()); - thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_data->image = std::make_unique<mgl::Image>(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return; } @@ -161,7 +170,7 @@ namespace QuickMedia { thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; } else { fprintf(stderr, "Failed to get first frame of %s\n", thumbnail_path.data.c_str()); - thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_data->image = std::make_unique<mgl::Image>(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; } return; @@ -171,7 +180,7 @@ namespace QuickMedia { thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; } else { fprintf(stderr, "Failed to convert %s to a thumbnail\n", thumbnail_path.data.c_str()); - thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_data->image = std::make_unique<mgl::Image>(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; } } @@ -190,7 +199,7 @@ namespace QuickMedia { if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) { fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str()); - thumbnail_load_data.thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_load_data.thumbnail_data->image = std::make_unique<mgl::Image>(); thumbnail_load_data.thumbnail_data->loading_state = LoadingState::READY_TO_LOAD; return; } @@ -208,14 +217,14 @@ namespace QuickMedia { } static void load_processed_thumbnail(ThumbnailLoadData &thumbnail_load_data) { - thumbnail_load_data.thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_load_data.thumbnail_data->image = std::make_unique<mgl::Image>(); Path thumbnail_path_resized = thumbnail_load_data.thumbnail_path; if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) thumbnail_path_resized.append("_" + std::to_string(thumbnail_load_data.resize_target_size.x) + "x" + std::to_string(thumbnail_load_data.resize_target_size.y)); if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) { - load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data); + thumbnail_load_data.thumbnail_data->image->load_from_file(thumbnail_path_resized.data.c_str()); thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return; } @@ -227,10 +236,10 @@ namespace QuickMedia { thumbnail_original_path = thumbnail_load_data.thumbnail_path; if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0) { - load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data); + thumbnail_load_data.thumbnail_data->image->load_from_file(thumbnail_path_resized.data.c_str()); thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; } else { - load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_original_path.data); + thumbnail_load_data.thumbnail_data->image->load_from_file(thumbnail_original_path.data.c_str()); thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; } } @@ -304,12 +313,12 @@ namespace QuickMedia { return exec_program_pipe(args, read_program) == 0; } - bool AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data, Path &thumbnail_path) { + bool AsyncImageLoader::load_thumbnail(const std::string &url, bool local, mgl::vec2i resize_target_size, std::shared_ptr<ThumbnailData> thumbnail_data, Path &thumbnail_path) { if(thumbnail_data->loading_state != LoadingState::NOT_LOADED) return true; if(url.empty()) { - thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_data->image = std::make_unique<mgl::Image>(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return false; } @@ -319,7 +328,7 @@ namespace QuickMedia { memset(&file_stat, 0, sizeof(file_stat)); if(stat(url.c_str(), &file_stat) != 0 || !S_ISREG(file_stat.st_mode)) { fprintf(stderr, "Failed to load thumbnail %s: no such file\n", url.c_str()); - thumbnail_data->image = std::make_unique<sf::Image>(); + thumbnail_data->image = std::make_unique<mgl::Image>(); thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; return false; } @@ -357,7 +366,7 @@ namespace QuickMedia { return true; } - std::shared_ptr<ThumbnailData> AsyncImageLoader::get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size) { + std::shared_ptr<ThumbnailData> AsyncImageLoader::get_thumbnail(const std::string &url, bool local, mgl::vec2i resize_target_size) { // TODO: Instead of generating a new hash everytime to access thumbnail, cache the hash of the thumbnail url auto &thumbnail_data = thumbnails[url]; if(!thumbnail_data) diff --git a/src/Body.cpp b/src/Body.cpp index eb7a1be..d8e4acd 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -6,12 +6,12 @@ #include "../include/Utils.hpp" #include "../include/Theme.hpp" #include "../include/StringUtils.hpp" +#include <mglpp/system/FloatRect.hpp> #include "../plugins/Plugin.hpp" #include <json/value.h> -#include <SFML/Graphics/CircleShape.hpp> -#include <SFML/Window/Event.hpp> -#include <SFML/Graphics/RenderWindow.hpp> -#include <SFML/Graphics/Shader.hpp> +#include <mglpp/window/Event.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Shader.hpp> #include <assert.h> #include <cmath> #include <malloc.h> @@ -41,7 +41,7 @@ namespace QuickMedia { static const int card_padding_x = 20 * get_config().scale; static const int card_padding_y = 20 * get_config().scale; static const int card_image_text_padding = 10 * get_config().scale; - static const sf::Vector2i card_max_image_size(card_width - card_padding_x * 2, (card_height - card_padding_y * 2) / 2); + static const mgl::vec2i card_max_image_size(card_width - card_padding_x * 2, (card_height - card_padding_y * 2) / 2); static const int num_columns_switch_to_list = 1; static const int embedded_item_border_width = 4; @@ -82,7 +82,7 @@ namespace QuickMedia { body_spacing[BODY_THEME_MODERN_SPACIOUS].embedded_item_font_size = std::floor(get_config().body.embedded_load_font_size * get_config().scale * get_config().font_scale); } - static void init_body_themes() { + void init_body_themes() { if(themes_initialized) return; @@ -91,38 +91,31 @@ namespace QuickMedia { themes_initialized = true; } - static sf::Vector2f to_vec2f(const sf::Vector2i &vec) { - return sf::Vector2f(vec.x, vec.y); - } - - Body::Body(BodyTheme body_theme, sf::Texture &loading_icon_texture, sf::Shader *rounded_rectangle_shader, sf::Shader *rounded_rectangle_mask_shader) : + Body::Body(BodyTheme body_theme, mgl::Texture &loading_icon_texture, mgl::Shader *rounded_rectangle_shader, mgl::Shader *rounded_rectangle_mask_shader) : draw_thumbnails(true), body_item_render_callback(nullptr), thumbnail_mask_shader(nullptr), body_theme(body_theme), selected_item(0), prev_selected_item(0), - loading_icon(loading_icon_texture), - progress_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(get_config().body.progress_font_size * get_config().scale * get_config().font_scale)), - replies_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(get_config().body.replies_font_size * get_config().scale * get_config().font_scale)), + loading_icon(&loading_icon_texture), + progress_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, get_config().body.progress_font_size * get_config().scale * get_config().font_scale)), + replies_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, get_config().body.replies_font_size * get_config().scale * get_config().font_scale)), + embedded_item_load_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, body_spacing[body_theme].embedded_item_font_size)), num_visible_items(0), top_cut_off(false), bottom_cut_off(false), - item_background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), - reaction_background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, rounded_rectangle_shader), + item_background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), + reaction_background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, rounded_rectangle_shader), rounded_rectangle_shader(rounded_rectangle_shader), rounded_rectangle_mask_shader(rounded_rectangle_mask_shader) { - assert(rounded_rectangle_shader); - assert(rounded_rectangle_mask_shader); - init_body_themes(); - embedded_item_load_text = sf::Text("", *FontLoader::get_font(FontLoader::FontType::LATIN), body_spacing[body_theme].embedded_item_font_size); - progress_text.setFillColor(get_theme().text_color); - replies_text.setFillColor(get_theme().replies_text_color); + progress_text.set_color(get_theme().text_color); + replies_text.set_color(get_theme().replies_text_color); thumbnail_max_size.x = 600; thumbnail_max_size.y = 337; - sf::Vector2f loading_icon_size(loading_icon.getTexture()->getSize().x, loading_icon.getTexture()->getSize().y); - loading_icon.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); + mgl::vec2f loading_icon_size(loading_icon.get_texture()->get_size().x, loading_icon.get_texture()->get_size().y); + loading_icon.set_origin(mgl::vec2f(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f)); render_selected_item_bg = !is_touch_enabled(); } @@ -480,23 +473,23 @@ namespace QuickMedia { } } - bool Body::on_event(const sf::RenderWindow &window, const sf::Event &event, bool keyboard_navigation) { - if(keyboard_navigation && event.type == sf::Event::KeyPressed && !event.key.alt) { + bool Body::on_event(const mgl::Window &window, const mgl::Event &event, bool keyboard_navigation) { + if(keyboard_navigation && event.type == mgl::Event::KeyPressed && !event.key.alt) { const bool rendering_card_view = card_view && card_view_enabled; - if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) { + if(event.key.code == mgl::Keyboard::Up || (event.key.control && event.key.code == mgl::Keyboard::K)) { render_selected_item_bg = true; bool top_reached = select_previous_item(true); if(!top_reached && on_top_reached) on_top_reached(); return true; - } else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) { + } else if(event.key.code == mgl::Keyboard::Down || (event.key.control && event.key.code == mgl::Keyboard::J)) { render_selected_item_bg = true; bool bottom_reached = select_next_item(true); if(!bottom_reached && on_bottom_reached) on_bottom_reached(); return true; - } else if(rendering_card_view && selected_column > 0 && ((!event.key.control && event.key.code == sf::Keyboard::Left) || (event.key.control && event.key.code == sf::Keyboard::H))) { + } else if(rendering_card_view && selected_column > 0 && ((!event.key.control && event.key.code == mgl::Keyboard::Left) || (event.key.control && event.key.code == mgl::Keyboard::H))) { render_selected_item_bg = true; const int new_selected_item = get_previous_visible_item(selected_item); if(new_selected_item != -1) { @@ -505,7 +498,7 @@ namespace QuickMedia { on_top_reached(); } return true; - } else if(rendering_card_view && selected_column + 1 < num_columns && ((!event.key.control && event.key.code == sf::Keyboard::Right) || (event.key.control && event.key.code == sf::Keyboard::L))) { + } else if(rendering_card_view && selected_column + 1 < num_columns && ((!event.key.control && event.key.code == mgl::Keyboard::Right) || (event.key.control && event.key.code == mgl::Keyboard::L))) { render_selected_item_bg = true; const int new_selected_item = get_next_visible_item(selected_item); if(new_selected_item != -1) { @@ -514,25 +507,25 @@ namespace QuickMedia { on_bottom_reached(); } return true; - } else if(event.key.code == sf::Keyboard::Home) { + } else if(event.key.code == mgl::Keyboard::Home) { render_selected_item_bg = true; select_first_item(false); if(on_top_reached) on_top_reached(); return true; - } else if(event.key.code == sf::Keyboard::End) { + } else if(event.key.code == mgl::Keyboard::End) { render_selected_item_bg = true; select_last_item(); if(on_bottom_reached) on_bottom_reached(); return true; - } else if(event.key.code == sf::Keyboard::PageUp) { + } else if(event.key.code == mgl::Keyboard::PageUp) { render_selected_item_bg = true; bool top_reached = select_previous_page(); if(!top_reached && on_top_reached) on_top_reached(); return true; - } else if(event.key.code == sf::Keyboard::PageDown) { + } else if(event.key.code == mgl::Keyboard::PageDown) { render_selected_item_bg = true; bool bottom_reached = select_next_page(); if(!bottom_reached && on_bottom_reached) @@ -544,11 +537,11 @@ namespace QuickMedia { if(!is_touch_enabled()) return false; - if(event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left && !mouse_left_pressed && sf::FloatRect(body_pos, body_size).contains(sf::Vector2f(event.mouseButton.x, event.mouseButton.y))) { + if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left && !mouse_left_pressed && mgl::FloatRect(body_pos, body_size).contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) { mouse_left_pressed = true; - mouse_pos_raw.x = event.mouseButton.x; - mouse_pos_raw.y = event.mouseButton.y; - mouse_pos = sf::Vector2f(mouse_pos_raw.x, mouse_pos_raw.y); + mouse_pos_raw.x = event.mouse_button.x; + mouse_pos_raw.y = event.mouse_button.y; + mouse_pos = mgl::vec2f(mouse_pos_raw.x, mouse_pos_raw.y); prev_mouse_pos_raw = mouse_pos_raw; mouse_click_pos = mouse_pos; mouse_press_pixels_moved_abs = 0.0; @@ -557,20 +550,20 @@ namespace QuickMedia { click_counts = std::abs(mouse_scroll_accel.y) < 5.0f; // Touching the body while it scrolls should stop it, not select the touched item body_swipe_x = 0.0; body_swipe_move_right = false; - grabbed_left_side = (event.mouseButton.x < body_pos.x + body_size.x * 0.15f); + grabbed_left_side = (event.mouse_button.x < body_pos.x + body_size.x * 0.15f); return true; - } else if(event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left && mouse_left_pressed) { + } else if(event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Left && mouse_left_pressed) { mouse_left_pressed = false; mouse_left_clicked = true; - mouse_release_pos = sf::Vector2f(event.mouseButton.x, event.mouseButton.y); - if(event.mouseButton.x > body_pos.x + body_size.x * 0.5f || mouse_scroll_accel.x > 20.0f) + mouse_release_pos = mgl::vec2f(event.mouse_button.x, event.mouse_button.y); + if(event.mouse_button.x > body_pos.x + body_size.x * 0.5f || mouse_scroll_accel.x > 20.0f) body_swipe_move_right = true; return true; - } else if(event.type == sf::Event::MouseMoved && mouse_left_pressed) { - sf::Vector2i mouse_pos_diff(event.mouseMove.x - mouse_pos_raw.x, event.mouseMove.y - mouse_pos_raw.y); + } else if(event.type == mgl::Event::MouseMoved && mouse_left_pressed) { + mgl::vec2i mouse_pos_diff(event.mouse_move.x - mouse_pos_raw.x, event.mouse_move.y - mouse_pos_raw.y); mouse_press_pixels_moved_abs += std::sqrt(mouse_pos_diff.x*mouse_pos_diff.x + mouse_pos_diff.y*mouse_pos_diff.y); - mouse_pos_raw.x = event.mouseMove.x; - mouse_pos_raw.y = event.mouseMove.y; + mouse_pos_raw.x = event.mouse_move.x; + mouse_pos_raw.y = event.mouse_move.y; render_selected_item_bg = false; return true; } @@ -578,28 +571,27 @@ namespace QuickMedia { return false; } - void Body::draw_drop_shadow(sf::RenderWindow &window) { + void Body::draw_drop_shadow(mgl::Window &window) { if(!show_drop_shadow) return; - const sf::Color color(0, 0, 0, 50); + const mgl::Color color(0, 0, 0, 50); const float height = 5.0f; - sf::Vertex gradient_points[4]; - gradient_points[0] = sf::Vertex(body_pos + sf::Vector2f(0.0f, 0.0f), color); - gradient_points[1] = sf::Vertex(body_pos + sf::Vector2f(body_size.x, 0.0f), color); - gradient_points[2] = sf::Vertex(body_pos + sf::Vector2f(body_size.x, height), sf::Color(color.r, color.g, color.b, 0)); - gradient_points[3] = sf::Vertex(body_pos + sf::Vector2f(0.0f, height), sf::Color(color.r, color.g, color.b, 0)); - window.draw(gradient_points, 4, sf::Quads); + mgl::Vertex gradient_points[4]; + gradient_points[0] = mgl::Vertex(body_pos + mgl::vec2f(0.0f, 0.0f), color); + gradient_points[1] = mgl::Vertex(body_pos + mgl::vec2f(body_size.x, 0.0f), color); + gradient_points[2] = mgl::Vertex(body_pos + mgl::vec2f(body_size.x, height), mgl::Color(color.r, color.g, color.b, 0)); + gradient_points[3] = mgl::Vertex(body_pos + mgl::vec2f(0.0f, height), mgl::Color(color.r, color.g, color.b, 0)); + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); } - double Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size) { + double Body::draw(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size) { return draw(window, pos, size, Json::Value::nullSingleton()); } // TODO: Use a render target for the whole body so all images can be put into one. - // TODO: Load thumbnails with more than one thread. - double Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress) { + double Body::draw(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, const Json::Value &content_progress) { const bool rendering_card_view = card_view && card_view_enabled; body_size_changed = std::abs(size.x - body_size.x) > 0.1f || std::abs(size.y - body_size.y) > 0.1f; @@ -619,7 +611,7 @@ namespace QuickMedia { if(attach_side == AttachSide::TOP) pos.y += body_spacing[body_theme].body_padding_vertical; - float frame_time = frame_timer.restart().asSeconds(); + float frame_time = frame_timer.restart(); if(frame_time > 1.0f) frame_time = 1.0f; @@ -633,7 +625,7 @@ namespace QuickMedia { prev_selected_item = selected_item; } - elapsed_time_sec = draw_timer.getElapsedTime().asSeconds(); + elapsed_time_sec = draw_timer.get_elapsed_time_seconds(); const bool prev_items_cut_off = top_cut_off || bottom_cut_off; const int prev_first_visible_item = first_visible_item; @@ -671,14 +663,14 @@ namespace QuickMedia { } if(is_touch_enabled()) { - const sf::Vector2f mouse_pos_diff(mouse_pos_raw.x - mouse_pos.x, mouse_pos_raw.y - mouse_pos.y); + const mgl::vec2f mouse_pos_diff(mouse_pos_raw.x - mouse_pos.x, mouse_pos_raw.y - mouse_pos.y); const float move_speed = 35.0f; - sf::Vector2f prev_mouse_pos = mouse_pos; + mgl::vec2f prev_mouse_pos = mouse_pos; mouse_pos.x += (mouse_pos_diff.x * std::min(1.0f, frame_time * move_speed)); mouse_pos.y += (mouse_pos_diff.y * std::min(1.0f, frame_time * move_speed)); - const sf::Vector2f mouse_pos_diff_smooth = mouse_pos - prev_mouse_pos; + const mgl::vec2f mouse_pos_diff_smooth = mouse_pos - prev_mouse_pos; - sf::Vector2f mouse_pos_raw_diff(mouse_pos_raw.x - prev_mouse_pos_raw.x, mouse_pos_raw.y - prev_mouse_pos_raw.y); + mgl::vec2f mouse_pos_raw_diff(mouse_pos_raw.x - prev_mouse_pos_raw.x, mouse_pos_raw.y - prev_mouse_pos_raw.y); prev_mouse_pos_raw = mouse_pos_raw; if(prev_items_cut_off) { @@ -687,7 +679,7 @@ namespace QuickMedia { if(body_swipe_x < 0.0) body_swipe_x = 0.0; page_scroll += mouse_pos_diff_smooth.y; - mouse_scroll_accel = sf::Vector2f(mouse_pos_raw_diff.x, mouse_pos_raw_diff.y) / (frame_time * 120.0f); + mouse_scroll_accel = mgl::vec2f(mouse_pos_raw_diff.x, mouse_pos_raw_diff.y) / (frame_time * 120.0f); } else { page_scroll += mouse_scroll_accel.y; } @@ -745,13 +737,12 @@ namespace QuickMedia { selected_scrolled = 0.0f; } - const sf::Vector2u window_size = window.getSize(); - const sf::View prev_view = window.getView(); + const mgl::vec2i window_size = window.get_size(); + const mgl::View prev_view = window.get_view(); if(!rendering_card_view) { - sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, size.y)); - new_view.setViewport(sf::FloatRect(0.0f, scissor_y / (float)window_size.y, 1.0f, size.y / (float)window_size.y)); - window.setView(new_view); + mgl::View new_view = { mgl::vec2i(0, scissor_y), mgl::vec2i(window_size.x, size.y) }; + window.set_view(new_view); } bool instant_move = body_size_changed; @@ -762,32 +753,34 @@ namespace QuickMedia { const float speed = 30.0f; - const sf::Vector2f item_background_size_diff = sf::Vector2f(item_background_target_size.x, item_background_target_height) - item_background_prev_size; + const mgl::vec2f item_background_size_diff = mgl::vec2f(item_background_target_size.x, item_background_target_height) - item_background_prev_size; const float item_background_size_speed = instant_move ? 1000.0f : speed; - const sf::Vector2f item_background_new_size = item_background_prev_size + (item_background_size_diff * std::min(1.0f, frame_time * item_background_size_speed)); + const mgl::vec2f item_background_new_size = item_background_prev_size + (item_background_size_diff * std::min(1.0f, frame_time * item_background_size_speed)); item_background_prev_size = item_background_new_size; - const sf::Vector2f item_background_pos_diff = item_background_target_pos - item_background_prev_pos; + const mgl::vec2f item_background_pos_diff = item_background_target_pos - item_background_prev_pos; const float item_background_move_speed = instant_move ? 1000.0f : speed; - sf::Vector2f item_background_new_pos = item_background_prev_pos + (item_background_pos_diff * std::min(1.0f, frame_time * item_background_move_speed)); + mgl::vec2f item_background_new_pos = item_background_prev_pos + (item_background_pos_diff * std::min(1.0f, frame_time * item_background_move_speed)); if(selected_item_fits_in_body) { item_background_new_pos.y = std::min(item_background_new_pos.y, size.y - item_background_new_size.y); item_background_new_pos.y = std::max(item_background_new_pos.y, 0.0f); } item_background_prev_pos = item_background_new_pos; + bool switched_to_list_view = false; double body_total_height = 0.0; if(rendering_card_view) { - body_total_height = draw_card_view(window, pos, size, window_size, scissor_y); + body_total_height = draw_card_view(window, pos, size, window_size, scissor_y, switched_to_list_view); } else { body_total_height = draw_list_view(window, pos, size, content_progress); } - window.setView(prev_view); - draw_drop_shadow(window); + window.set_view(prev_view); + if(!switched_to_list_view) + draw_drop_shadow(window); const double scrolling_bar_height_ratio = body_total_height == 0.0 ? 0.0 : (size.y / body_total_height); - if(scrolling_bar_height_ratio <= 1.0) { + if(!switched_to_list_view && scrolling_bar_height_ratio <= 1.0) { const double scrollbar_max_height = size.y - body_spacing[body_theme].body_padding_vertical * 2.0f; double scrollbar_offset_y = body_total_height == 0.0 ? 0.0 : (std::abs(page_scroll) / body_total_height); @@ -808,7 +801,7 @@ namespace QuickMedia { scrollbar_y = std::floor(scissor_y + body_spacing[body_theme].body_padding_vertical + scrollbar_max_height - scrollbar.get_size().y - scrollbar_offset_y * scrollbar_max_height); scrollbar.set_position( - sf::Vector2f(pos.x + scrollbar_offset_x, scrollbar_y)); + mgl::vec2f(pos.x + scrollbar_offset_x, scrollbar_y)); scrollbar.draw(window); } @@ -873,41 +866,37 @@ namespace QuickMedia { void Body::update_dirty_state(BodyItem *body_item, float width) { if((body_item->dirty && !body_item->get_title().empty()) || (body_size_changed && body_item->title_text)) { body_item->dirty = false; - // TODO: Find a way to optimize fromUtf8 - sf::String str = sf::String::fromUtf8(body_item->get_title().begin(), body_item->get_title().end()); if(body_item->title_text) { - body_item->title_text->setString(std::move(str)); + body_item->title_text->setString(body_item->get_title()); body_item->title_text->setMaxWidth(width); } else { - body_item->title_text = std::make_unique<Text>(std::move(str), false, std::floor(get_config().body.title_font_size * get_config().scale * get_config().font_scale), width, title_mark_urls); + body_item->title_text = std::make_unique<Text>(body_item->get_title(), false, std::floor(get_config().body.title_font_size * get_config().scale * get_config().font_scale), width, title_mark_urls); } - body_item->title_text->setFillColor(body_item->get_title_color()); + body_item->title_text->set_color(body_item->get_title_color()); body_item->title_text->updateGeometry(); } if((body_item->dirty_description && !body_item->get_description().empty()) || (body_size_changed && body_item->description_text)) { body_item->dirty_description = false; - sf::String str = sf::String::fromUtf8(body_item->get_description().begin(), body_item->get_description().end()); if(body_item->description_text) { - body_item->description_text->setString(std::move(str)); + body_item->description_text->setString(body_item->get_description()); body_item->description_text->setMaxWidth(width); } else { - body_item->description_text = std::make_unique<Text>(std::move(str), false, std::floor(get_config().body.description_font_size * get_config().scale * get_config().font_scale), width, true); + body_item->description_text = std::make_unique<Text>(body_item->get_description(), false, std::floor(get_config().body.description_font_size * get_config().scale * get_config().font_scale), width, true); } - body_item->description_text->setFillColor(body_item->get_description_color()); + body_item->description_text->set_color(body_item->get_description_color()); body_item->description_text->updateGeometry(); } if((body_item->dirty_author && !body_item->get_author().empty()) || (body_size_changed && body_item->author_text)) { body_item->dirty_author = false; - sf::String str = sf::String::fromUtf8(body_item->get_author().begin(), body_item->get_author().end()); if(body_item->author_text) { - body_item->author_text->setString(std::move(str)); + body_item->author_text->setString(body_item->get_author()); body_item->author_text->setMaxWidth(width); } else { - body_item->author_text = std::make_unique<Text>(std::move(str), true, std::floor(get_config().body.author_font_size * get_config().scale * get_config().font_scale), width); + body_item->author_text = std::make_unique<Text>(body_item->get_author(), true, std::floor(get_config().body.author_font_size * get_config().scale * get_config().font_scale), width); } - body_item->author_text->setFillColor(body_item->get_author_color()); + body_item->author_text->set_color(body_item->get_author_color()); body_item->author_text->updateGeometry(); } @@ -928,12 +917,12 @@ namespace QuickMedia { strftime(time_str, sizeof(time_str) - 1, "%Y %b %d, %a %H:%M", &message_tm); if(body_item->timestamp_text) { - body_item->timestamp_text->setString(time_str); + body_item->timestamp_text->set_string(time_str); } else { - body_item->timestamp_text = std::make_unique<sf::Text>(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(get_config().body.timestamp_font_size * get_config().scale * get_config().font_scale)); + body_item->timestamp_text = std::make_unique<mgl::Text>(time_str, *FontLoader::get_font(FontLoader::FontType::LATIN, get_config().body.timestamp_font_size * get_config().scale * get_config().font_scale)); } - body_item->timestamp_text->setFillColor(get_theme().timestamp_text_color); + body_item->timestamp_text->set_color(get_theme().timestamp_text_color); } } } @@ -959,13 +948,13 @@ namespace QuickMedia { body_item->keep_alive_frames = 0; } - sf::Vector2i Body::get_item_thumbnail_size(BodyItem *item) const { - sf::Vector2i content_size; - sf::Vector2i thumbnail_max_size_scaled = (card_view && card_view_enabled) ? card_max_image_size : sf::Vector2i(thumbnail_max_size.x * get_config().scale, thumbnail_max_size.y * get_config().scale); + mgl::vec2i Body::get_item_thumbnail_size(BodyItem *item) const { + mgl::vec2i content_size; + mgl::vec2i thumbnail_max_size_scaled = (card_view && card_view_enabled) ? card_max_image_size : mgl::vec2i(thumbnail_max_size.x * get_config().scale, thumbnail_max_size.y * get_config().scale); if(item->thumbnail_size.x > 0 && item->thumbnail_size.y > 0) - content_size = clamp_to_size(sf::Vector2i(std::floor(item->thumbnail_size.x * get_config().scale), std::floor(item->thumbnail_size.y * get_config().scale)), thumbnail_max_size_scaled); + content_size = clamp_to_size(mgl::vec2i(std::floor(item->thumbnail_size.x * get_config().scale), std::floor(item->thumbnail_size.y * get_config().scale)), thumbnail_max_size_scaled); else - content_size = sf::Vector2i(250 * get_config().scale, 141 * get_config().scale); + content_size = mgl::vec2i(250 * get_config().scale, 141 * get_config().scale); return content_size; } @@ -987,7 +976,7 @@ namespace QuickMedia { return -1; } - void Body::draw_item(sf::RenderWindow &window, std::shared_ptr<BodyItem> &item, sf::Vector2f pos, sf::Vector2f size, bool include_embedded_item, bool is_embedded) { + void Body::draw_item(mgl::Window &window, std::shared_ptr<BodyItem> &item, mgl::vec2f pos, mgl::vec2f size, bool include_embedded_item, bool is_embedded) { // TODO: What about when |card_view| is used? item->keep_alive_frames = 3; get_item_height(item.get(), size.x, true, false, false, -1); @@ -1010,9 +999,9 @@ namespace QuickMedia { return ""; } - void Body::handle_item_render(const sf::Vector2f pos, const float item_width, const float item_height, int item_index) { + void Body::handle_item_render(const mgl::vec2f pos, const float item_width, const float item_height, int item_index) { if(body_item_select_callback && mouse_left_clicked && !clicked_body_item && click_counts && std::abs(mouse_scroll_accel.y) < 5.0f) { - sf::FloatRect item_box(pos + body_pos, sf::Vector2f(item_width, item_height)); + mgl::FloatRect item_box(pos + body_pos, mgl::vec2f(item_width, item_height)); if(item_box.contains(mouse_click_pos) && item_box.contains(mouse_release_pos) && mouse_press_pixels_moved_abs <= 25.0) { clicked_body_item = items[item_index]; set_selected_item(item_index, false); @@ -1021,7 +1010,7 @@ namespace QuickMedia { if(item_index == selected_item) { item_background_target_pos = pos; - item_background_target_size = sf::Vector2f(item_width, item_height); + item_background_target_size = mgl::vec2f(item_width, item_height); item_background_target_height = item_height; if(target_set == TargetSetState::NOT_SET) target_set = TargetSetState::SET; @@ -1032,11 +1021,11 @@ namespace QuickMedia { return std::min(max, std::max(min, value)); } - static sf::Vector2f round(sf::Vector2f vec) { + static mgl::vec2f round(mgl::vec2f vec) { return { std::floor(vec.x), std::floor(vec.y) }; } - double Body::draw_list_view(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress) { + double Body::draw_list_view(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, const Json::Value &content_progress) { const int num_items = items.size(); const float pos_y_before_scroll = pos.y; @@ -1180,7 +1169,7 @@ namespace QuickMedia { if(selected_item_fits_in_body) { item_background.set_position(round( - sf::Vector2f( + mgl::vec2f( item_background_prev_pos.x, clamp(item_background_prev_pos.y, pos_y_before_scroll, pos_y_before_scroll + size.y - item_background_prev_size.y - body_spacing[body_theme].body_padding_vertical + offset_y_spacing)))); } else { @@ -1202,7 +1191,8 @@ namespace QuickMedia { return std::abs(pos.y - pos_y_start); } - double Body::draw_card_view(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, sf::Vector2u window_size, float scissor_y) { + double Body::draw_card_view(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::vec2i window_size, float scissor_y, bool &switched_to_list_view) { + switched_to_list_view = false; num_columns = size.x / card_width; int space_left_column = size.x - (num_columns * card_width); @@ -1223,6 +1213,7 @@ namespace QuickMedia { card_view_enabled = false; const float body_total_height = draw(window, body_pos, body_size); card_view_enabled = true; + switched_to_list_view = true; return body_total_height; } @@ -1238,7 +1229,7 @@ namespace QuickMedia { int row_max_height = 0; const int num_items = items.size(); const float pos_y_start = page_scroll; - sf::Vector2f pos_offset(space_left_column_each, page_scroll); + mgl::vec2f pos_offset(space_left_column_each, page_scroll); while(item_index < num_items) { std::shared_ptr<BodyItem> &item = items[item_index]; @@ -1269,7 +1260,7 @@ namespace QuickMedia { if(body_item_render_callback) body_item_render_callback(item); - sf::Vector2i thumbnail_size = get_item_thumbnail_size(item.get()); + mgl::vec2i thumbnail_size = get_item_thumbnail_size(item.get()); std::shared_ptr<ThumbnailData> item_thumbnail; if(draw_thumbnails && !item->thumbnail_url.empty()) item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, thumbnail_size); @@ -1288,10 +1279,6 @@ namespace QuickMedia { render_item.item_thumbnail = item_thumbnail; render_items.push_back(std::move(render_item)); - sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, size.y)); - new_view.setViewport(sf::FloatRect(0.0f, scissor_y / (float)window_size.y, 1.0f, size.y / (float)window_size.y)); - window.setView(new_view); - handle_item_render(pos + pos_offset, card_width, item_height, item_index); ++num_visible_items; @@ -1319,7 +1306,7 @@ namespace QuickMedia { if(drawn_column_index == num_columns) { if(row_has_selected_item) - item_background_target_size = sf::Vector2f(card_width, row_max_height); + item_background_target_size = mgl::vec2f(card_width, row_max_height); drawn_column_index = 0; ++num_visible_rows; @@ -1333,14 +1320,13 @@ namespace QuickMedia { ++item_index; } - sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, size.y)); - new_view.setViewport(sf::FloatRect(0.0f, scissor_y / (float)window_size.y, 1.0f, size.y / (float)window_size.y)); - window.setView(new_view); + mgl::View new_view = { mgl::vec2i(0, scissor_y), mgl::vec2i(window_size.x, size.y) }; + window.set_view(new_view); for(RenderItem &render_item : render_items) { if(render_item.item_index == selected_item) { item_background.set_position(round( - sf::Vector2f( + mgl::vec2f( item_background_prev_pos.x, clamp(item_background_prev_pos.y, pos.y, pos.y + size.y - item_background_prev_size.y - body_spacing[body_theme].body_padding_vertical - body_spacing[body_theme].spacing_y)))); item_background.set_size(round(item_background_prev_size)); @@ -1351,29 +1337,29 @@ namespace QuickMedia { } for(RenderItem &render_item : render_items) { - draw_card_item(window, render_item.body_item, render_item.pos, render_item.pos_offset, size, sf::Vector2f(window_size.x, window_size.y), render_item.item_height, scissor_y, render_item.item_index, render_item.item_thumbnail.get()); + draw_card_item(window, render_item.body_item, render_item.pos, render_item.pos_offset, size, mgl::vec2f(window_size.x, window_size.y), render_item.item_height, scissor_y, render_item.item_index, render_item.item_thumbnail.get()); } render_items.clear(); if(row_has_selected_item) - item_background_target_size = sf::Vector2f(card_width, row_max_height); + item_background_target_size = mgl::vec2f(card_width, row_max_height); return std::abs(pos_offset.y - pos_y_start); } - void Body::draw_item(sf::RenderWindow &window, std::shared_ptr<BodyItem> &item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item, bool merge_with_previous) { - sf::Vector2i thumbnail_size = get_item_thumbnail_size(item.get()); + void Body::draw_item(mgl::Window &window, std::shared_ptr<BodyItem> &item, const mgl::vec2f &pos, const mgl::vec2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item, bool merge_with_previous) { + mgl::vec2i thumbnail_size = get_item_thumbnail_size(item.get()); std::shared_ptr<ThumbnailData> item_thumbnail; if(draw_thumbnails && !merge_with_previous && !item->thumbnail_url.empty()) item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, thumbnail_size); - thumbnail_size = clamp_to_size_x(thumbnail_size, sf::Vector2i(size.x - body_spacing[body_theme].image_padding_x * 2.0f, thumbnail_size.y)); + thumbnail_size = clamp_to_size_x(thumbnail_size, size.x - body_spacing[body_theme].image_padding_x * 2.0f); if(body_item_render_callback && include_embedded_item) body_item_render_callback(item); - sf::Vector2f item_pos; + mgl::vec2f item_pos; item_pos.x = std::floor(pos.x); item_pos.y = std::floor(pos.y); @@ -1382,51 +1368,52 @@ namespace QuickMedia { float text_offset_x = body_spacing[body_theme].padding_x; if(item_thumbnail && !merge_with_previous) { // TODO: Verify if this is safe. The thumbnail is being modified in another thread - if(item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { - image.setTexture(item_thumbnail->texture, true); - auto image_size = image.getTexture()->getSize(); - sf::Vector2f image_size_f(image_size.x, image_size.y); - sf::Vector2f content_size = to_vec2f(thumbnail_size); + if(item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.is_valid()) { + image.set_texture(&item_thumbnail->texture); + auto image_size = image.get_texture()->get_size(); + mgl::vec2f image_size_f(image_size.x, image_size.y); + mgl::vec2f content_size = thumbnail_size.to_vec2f(); auto new_image_size = clamp_to_size(image_size_f, content_size); auto image_scale = get_ratio(image_size_f, new_image_size); - image.setScale(image_scale); - image.setPosition(item_pos + sf::Vector2f(body_spacing[body_theme].image_padding_x, padding_y)); + image.set_scale(image_scale); + image.set_position(item_pos + mgl::vec2f(body_spacing[body_theme].image_padding_x, padding_y)); if(thumbnail_mask_shader && item->thumbnail_mask_type == ThumbnailMaskType::CIRCLE) { - thumbnail_mask_shader->setUniform("resolution", new_image_size); + thumbnail_mask_shader->set_uniform("resolution", new_image_size); window.draw(image, thumbnail_mask_shader); } else if(rounded_rectangle_mask_shader) { - rounded_rectangle_mask_shader->setUniform("radius", 10.0f); - rounded_rectangle_mask_shader->setUniform("resolution", new_image_size); + rounded_rectangle_mask_shader->set_uniform("radius", 10.0f); + rounded_rectangle_mask_shader->set_uniform("resolution", new_image_size); window.draw(image, rounded_rectangle_mask_shader); } else { window.draw(image); } text_offset_x += body_spacing[body_theme].image_padding_x + new_image_size.x; // We want the next image fallback to have the same size as the successful image rendering, because its likely the image fallback will have the same size (for example thumbnails on youtube) - //image_fallback.setSize(sf::Vector2f(width_ratio * image_size.x, height_ratio * image_size.y)); + //image_fallback.set_size(mgl::vec2f(width_ratio * image_size.x, height_ratio * image_size.y)); } else if(!item->thumbnail_url.empty()) { - sf::Vector2f content_size = to_vec2f(thumbnail_size); + mgl::vec2f content_size = thumbnail_size.to_vec2f(); if(thumbnail_mask_shader && item->thumbnail_mask_type == ThumbnailMaskType::CIRCLE) { // TODO: Use the mask shader instead, but a vertex shader is also needed for that to pass the vertex coordinates since // shapes dont have texture coordinates. // TODO: Cache circle shape - sf::CircleShape circle_shape(content_size.x * 0.5f); - circle_shape.setFillColor(get_theme().image_loading_background_color); - circle_shape.setPosition(item_pos + sf::Vector2f(body_spacing[body_theme].image_padding_x, padding_y)); - window.draw(circle_shape); + // TODO: Fix + //sf::CircleShape circle_shape(content_size.x * 0.5f); + //circle_shape.set_color(get_theme().image_loading_background_color); + //circle_shape.set_position(item_pos + mgl::vec2f(body_spacing[body_theme].image_padding_x, padding_y)); + //window.draw(circle_shape); } else { - image_fallback.setSize(content_size); - image_fallback.setFillColor(get_theme().image_loading_background_color); - image_fallback.setPosition(item_pos + sf::Vector2f(body_spacing[body_theme].image_padding_x, padding_y)); + image_fallback.set_size(content_size); + image_fallback.set_color(get_theme().image_loading_background_color); + image_fallback.set_position(item_pos + mgl::vec2f(body_spacing[body_theme].image_padding_x, padding_y)); window.draw(image_fallback); } - sf::Vector2f loading_icon_size(loading_icon.getTexture()->getSize().x, loading_icon.getTexture()->getSize().y); + mgl::vec2f loading_icon_size(loading_icon.get_texture()->get_size().x, loading_icon.get_texture()->get_size().y); auto new_loading_icon_size = clamp_to_size(loading_icon_size, content_size); - loading_icon.setPosition(item_pos + sf::Vector2f(body_spacing[body_theme].image_padding_x, padding_y) + (content_size * 0.5f)); - loading_icon.setScale(get_ratio(loading_icon_size, new_loading_icon_size)); - loading_icon.setRotation(elapsed_time_sec * 400.0); + loading_icon.set_position(item_pos + mgl::vec2f(body_spacing[body_theme].image_padding_x, padding_y) + (content_size * 0.5f)); + loading_icon.set_scale(get_ratio(loading_icon_size, new_loading_icon_size)); + loading_icon.set_rotation(elapsed_time_sec * 400.0); window.draw(loading_icon); text_offset_x += body_spacing[body_theme].image_padding_x + item->loaded_image_size.x; } @@ -1436,22 +1423,22 @@ namespace QuickMedia { const float text_offset_y = std::floor(6.0f * get_config().scale * get_config().font_scale); - const float timestamp_text_y = std::floor(item_pos.y + padding_y - text_offset_y); + const float timestamp_text_y = std::floor(item_pos.y + padding_y - text_offset_y - std::floor(4.0f * get_config().scale * get_config().font_scale)); if(item->author_text && !merge_with_previous) { - item->author_text->setPosition(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + padding_y - text_offset_y)); + item->author_text->set_position(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + padding_y - text_offset_y)); item->author_text->draw(window); - sf::Vector2f replies_text_pos = item->author_text->getPosition() + sf::Vector2f(0.0f, 5.0f); + mgl::vec2f replies_text_pos = item->author_text->get_position(); replies_text_pos.x += item->author_text->getWidth() + 5.0f; - replies_text.setPosition(replies_text_pos); + replies_text.set_position(replies_text_pos); - sf::String replies_text_str; + std::string replies_text_str; for(size_t reply_index : item->replies) { BodyItem *reply_item = items[reply_index].get(); replies_text_str += " >>"; replies_text_str += reply_item->post_number; } - replies_text.setString(std::move(replies_text_str)); + replies_text.set_string(std::move(replies_text_str)); window.draw(replies_text); item_pos.y += item->author_text->getHeight() - 2.0f + std::floor(3.0f * get_config().scale); @@ -1460,32 +1447,32 @@ namespace QuickMedia { if(include_embedded_item && item->embedded_item_status != FetchStatus::NONE) { const float embedded_item_width = std::floor(size.x - text_offset_x - embedded_item_border_width - body_spacing[body_theme].padding_x); float embedded_item_height = item->embedded_item ? get_item_height(item->embedded_item.get(), embedded_item_width, true, false) : ((body_spacing[body_theme].embedded_item_font_size + 5.0f) + body_spacing[body_theme].embedded_item_padding_y * 2.0f); - sf::RectangleShape border_left(sf::Vector2f(embedded_item_border_width, std::floor(embedded_item_height))); - border_left.setFillColor(get_theme().embedded_item_border_color); - border_left.setPosition(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + body_spacing[body_theme].embedded_item_padding_y + 2.0f)); + mgl::Rectangle border_left(mgl::vec2f(embedded_item_border_width, std::floor(embedded_item_height))); + border_left.set_color(get_theme().embedded_item_border_color); + border_left.set_position(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + body_spacing[body_theme].embedded_item_padding_y + 2.0f)); window.draw(border_left); if(item->embedded_item) { - sf::Vector2f embedded_item_pos(std::floor(item_pos.x + text_offset_x + embedded_item_border_width + body_spacing[body_theme].padding_x), std::floor(item_pos.y + body_spacing[body_theme].embedded_item_padding_y + 6.0f)); - sf::Vector2f embedded_item_size(embedded_item_width, embedded_item_height); + mgl::vec2f embedded_item_pos(std::floor(item_pos.x + text_offset_x + embedded_item_border_width + body_spacing[body_theme].padding_x), std::floor(item_pos.y + body_spacing[body_theme].embedded_item_padding_y + 6.0f)); + mgl::vec2f embedded_item_size(embedded_item_width, embedded_item_height); draw_item(window, item->embedded_item, embedded_item_pos, embedded_item_size, false, true); } else { - embedded_item_load_text.setString(embedded_item_status_to_string(item->embedded_item_status)); - embedded_item_load_text.setPosition(vec2f_floor(item_pos.x + text_offset_x + embedded_item_border_width + body_spacing[body_theme].padding_x, item_pos.y + embedded_item_height * 0.5f - (body_spacing[body_theme].embedded_item_font_size + 5.0f) * 0.5f + 6.0f)); + embedded_item_load_text.set_string(embedded_item_status_to_string(item->embedded_item_status)); + embedded_item_load_text.set_position(vec2f_floor(item_pos.x + text_offset_x + embedded_item_border_width + body_spacing[body_theme].padding_x, item_pos.y + embedded_item_height * 0.5f - (body_spacing[body_theme].embedded_item_font_size + 5.0f) * 0.5f + 6.0f)); window.draw(embedded_item_load_text); } item_pos.y += embedded_item_height + 4.0f; } if(item->title_text) { - item->title_text->setPosition(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + padding_y - text_offset_y)); + item->title_text->set_position(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + padding_y - text_offset_y)); item->title_text->draw(window); item_pos.y += item->title_text->getHeight() - 2.0f + std::floor(3.0f * get_config().scale); } if(item->description_text) { float height_offset = 0.0f; - item->description_text->setPosition(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + padding_y - text_offset_y + height_offset)); + item->description_text->set_position(vec2f_floor(item_pos.x + text_offset_x, item_pos.y + padding_y - text_offset_y + height_offset)); item->description_text->draw(window); item_pos.y += item->description_text->getHeight() - 2.0f; } @@ -1499,9 +1486,9 @@ namespace QuickMedia { auto &reaction = item->reactions[i]; reaction.text->updateGeometry(); reaction_max_height = std::max(reaction_max_height, reaction.text->getHeight()); - reaction.text->setPosition(vec2f_floor(item_pos.x + text_offset_x + reaction_offset_x + body_spacing[body_theme].reaction_background_padding_x, item_pos.y + padding_y - 4.0f + body_spacing[body_theme].reaction_background_padding_y)); + reaction.text->set_position(vec2f_floor(item_pos.x + text_offset_x + reaction_offset_x + body_spacing[body_theme].reaction_background_padding_x, item_pos.y + padding_y - 4.0f + body_spacing[body_theme].reaction_background_padding_y)); reaction_background.set_position(vec2f_floor(item_pos.x + text_offset_x + reaction_offset_x, item_pos.y + padding_y)); - reaction_background.set_size(sf::Vector2f(reaction.text->getWidth() + body_spacing[body_theme].reaction_background_padding_x * 2.0f, reaction.text->getHeight() + body_spacing[body_theme].reaction_background_padding_y * 2.0f)); + reaction_background.set_size(mgl::vec2f(reaction.text->getWidth() + body_spacing[body_theme].reaction_background_padding_x * 2.0f, reaction.text->getHeight() + body_spacing[body_theme].reaction_background_padding_y * 2.0f)); reaction_background.draw(window); reaction_offset_x += reaction.text->getWidth() + body_spacing[body_theme].reaction_background_padding_x * 2.0f + body_spacing[body_theme].reaction_spacing_x; reaction.text->draw(window); @@ -1515,7 +1502,7 @@ namespace QuickMedia { } if(item_index == selected_item && item->timestamp_text) { - item->timestamp_text->setPosition(vec2f_floor(item_pos.x + size.x - item->timestamp_text->getLocalBounds().width - body_spacing[body_theme].padding_x, timestamp_text_y + 8.0f)); + item->timestamp_text->set_position(vec2f_floor(item_pos.x + size.x - item->timestamp_text->get_bounds().size.x - body_spacing[body_theme].padding_x, timestamp_text_y + 8.0f)); window.draw(*item->timestamp_text); } @@ -1529,126 +1516,124 @@ namespace QuickMedia { const Json::Value ¤t_json = item_progress["current"]; const Json::Value &total_json = item_progress["total"]; if(current_json.isNumeric() && total_json.isNumeric()) { - progress_text.setString(std::string("Page: ") + std::to_string(current_json.asInt()) + "/" + std::to_string(total_json.asInt())); - auto bounds = progress_text.getLocalBounds(); - progress_text.setPosition(vec2f_floor(item_pos.x + size.x - bounds.width - body_spacing[body_theme].padding_x, timestamp_text_y + text_offset_y)); + progress_text.set_string(std::string("Page: ") + std::to_string(current_json.asInt()) + "/" + std::to_string(total_json.asInt())); + auto bounds = progress_text.get_bounds(); + progress_text.set_position(vec2f_floor(item_pos.x + size.x - bounds.size.x - body_spacing[body_theme].padding_x, timestamp_text_y + text_offset_y)); window.draw(progress_text); } } } - void Body::draw_card_item(sf::RenderWindow &window, std::shared_ptr<BodyItem> &item, const sf::Vector2f &pos, const sf::Vector2f &pos_offset, const sf::Vector2f &body_size, const sf::Vector2f &window_size, float item_height, float scissor_y, int item_index, ThumbnailData *item_thumbnail) { - const sf::Vector2i thumbnail_size = get_item_thumbnail_size(item.get()); + void Body::draw_card_item(mgl::Window &window, std::shared_ptr<BodyItem> &item, const mgl::vec2f &pos, const mgl::vec2f &pos_offset, const mgl::vec2f &body_size, const mgl::vec2f &window_size, float item_height, float scissor_y, int item_index, ThumbnailData *item_thumbnail) { + const mgl::vec2i thumbnail_size = get_item_thumbnail_size(item.get()); - sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, body_size.y)); - new_view.setViewport(sf::FloatRect(0.0f, scissor_y / (float)window_size.y, 1.0f, body_size.y / (float)window_size.y)); - window.setView(new_view); + mgl::View new_view = { mgl::vec2i(0, scissor_y), mgl::vec2i(window_size.x, body_size.y) }; + window.set_view(new_view); { float image_height = 0.0f; - if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { - image.setTexture(item_thumbnail->texture, true); - auto image_size = image.getTexture()->getSize(); - sf::Vector2f image_size_f(image_size.x, image_size.y); - sf::Vector2f content_size = to_vec2f(thumbnail_size); + if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.is_valid()) { + image.set_texture(&item_thumbnail->texture); + auto image_size = image.get_texture()->get_size(); + mgl::vec2f image_size_f(image_size.x, image_size.y); + mgl::vec2f content_size = thumbnail_size.to_vec2f(); auto new_image_size = clamp_to_size(image_size_f, content_size); auto image_scale = get_ratio(image_size_f, new_image_size); - image.setScale(image_scale); - image.setPosition(pos + pos_offset + sf::Vector2f(card_padding_x, card_padding_y) + sf::Vector2f(card_max_image_size.x * 0.5f, 0.0f) - sf::Vector2f(new_image_size.x * 0.5f, 0.0f)); + image.set_scale(image_scale); + image.set_position(pos + pos_offset + mgl::vec2f(card_padding_x, card_padding_y) + mgl::vec2f(card_max_image_size.x * 0.5f, 0.0f) - mgl::vec2f(new_image_size.x * 0.5f, 0.0f)); image_height = new_image_size.y; if(thumbnail_mask_shader && item->thumbnail_mask_type == ThumbnailMaskType::CIRCLE) { - thumbnail_mask_shader->setUniform("resolution", new_image_size); + thumbnail_mask_shader->set_uniform("resolution", new_image_size); window.draw(image, thumbnail_mask_shader); } else if(rounded_rectangle_mask_shader) { - rounded_rectangle_mask_shader->setUniform("radius", 10.0f); - rounded_rectangle_mask_shader->setUniform("resolution", new_image_size); + rounded_rectangle_mask_shader->set_uniform("radius", 10.0f); + rounded_rectangle_mask_shader->set_uniform("resolution", new_image_size); window.draw(image, rounded_rectangle_mask_shader); } else { window.draw(image); } } else if(!item->thumbnail_url.empty()) { - sf::Vector2f content_size = to_vec2f(thumbnail_size); - sf::Vector2f loading_icon_size(loading_icon.getTexture()->getSize().x, loading_icon.getTexture()->getSize().y); + mgl::vec2f content_size = thumbnail_size.to_vec2f(); + mgl::vec2f loading_icon_size(loading_icon.get_texture()->get_size().x, loading_icon.get_texture()->get_size().y); auto new_loading_icon_size = clamp_to_size(loading_icon_size, content_size); - loading_icon.setPosition(pos + pos_offset + sf::Vector2f(card_padding_x, card_padding_y) + sf::Vector2f(card_max_image_size.x, content_size.y) * 0.5f); - loading_icon.setScale(get_ratio(loading_icon_size, new_loading_icon_size)); - loading_icon.setRotation(elapsed_time_sec * 400.0); + loading_icon.set_position(pos + pos_offset + mgl::vec2f(card_padding_x, card_padding_y) + mgl::vec2f(card_max_image_size.x, content_size.y) * 0.5f); + loading_icon.set_scale(get_ratio(loading_icon_size, new_loading_icon_size)); + loading_icon.set_rotation(elapsed_time_sec * 400.0); window.draw(loading_icon); image_height = content_size.y; } const float text_padding = item_thumbnail ? card_image_text_padding : 0.0f; - sf::Vector2f text_pos = sf::Vector2f(pos.x, scissor_y + body_spacing[body_theme].body_padding_vertical) + pos_offset + sf::Vector2f(card_padding_x, card_padding_y) + sf::Vector2f(0.0f, image_height + text_padding); + mgl::vec2f text_pos = mgl::vec2f(pos.x, scissor_y + body_spacing[body_theme].body_padding_vertical) + pos_offset + mgl::vec2f(card_padding_x, card_padding_y) + mgl::vec2f(0.0f, image_height + text_padding); const float text_height = (item_height - card_padding_y * 2.0f) - image_height - text_padding; const float underflow_text = text_pos.y - scissor_y; const float underflow_height = underflow_text < 0.0f ? std::max(0.0f, text_height + underflow_text) : text_height; - sf::View new_view(sf::FloatRect(0.0f, 0.0f, window_size.x, underflow_height)); - new_view.setViewport(sf::FloatRect(0.0f, std::max(text_pos.y, scissor_y) / (float)window_size.y, 1.0f, underflow_height / (float)window_size.y)); - window.setView(new_view); + + mgl::View new_view = { mgl::vec2i(0, std::max(text_pos.y, scissor_y)), mgl::vec2i(window_size.x, underflow_height) }; + window.set_view(new_view); text_pos.y = std::min(0.0f, underflow_text); float text_offset_y = 0.0f; if(item->author_text) { - item->author_text->setPosition(text_pos); + item->author_text->set_position(text_pos); item->author_text->draw(window); text_offset_y += item->author_text->getHeight(); } if(item->title_text) { - item->title_text->setPosition(text_pos + sf::Vector2f(0.0f, text_offset_y)); + item->title_text->set_position(text_pos + mgl::vec2f(0.0f, text_offset_y)); item->title_text->draw(window); text_offset_y += item->title_text->getHeight(); } if(item->description_text) { - item->description_text->setPosition(text_pos + sf::Vector2f(0.0f, text_offset_y)); + item->description_text->set_position(text_pos + mgl::vec2f(0.0f, text_offset_y)); item->description_text->draw(window); text_offset_y += item->description_text->getHeight(); } const float gradient_height = 5.0f; if(text_offset_y >= text_height - gradient_height && std::abs(item_height - card_height) < 1) { - const sf::Vector2f card_bottom(text_pos.x, text_height); - const sf::Color color = item_index == selected_item ? get_theme().selected_color : get_theme().background_color; - - sf::Vertex gradient_points[4]; - gradient_points[0] = sf::Vertex(card_bottom + sf::Vector2f(0.0f, -gradient_height), sf::Color(color.r, color.g, color.b, 0)); - gradient_points[1] = sf::Vertex(card_bottom + sf::Vector2f(card_max_image_size.x, -gradient_height), sf::Color(color.r, color.g, color.b, 0)); - gradient_points[2] = sf::Vertex(card_bottom + sf::Vector2f(card_max_image_size.x, 0.0f), color); - gradient_points[3] = sf::Vertex(card_bottom + sf::Vector2f(0.0f, 0.0f), color); - window.draw(gradient_points, 4, sf::Quads); + const mgl::vec2f card_bottom(text_pos.x, text_height); + const mgl::Color color = item_index == selected_item ? get_theme().selected_color : get_theme().background_color; + + mgl::Vertex gradient_points[4]; + gradient_points[0] = mgl::Vertex(card_bottom + mgl::vec2f(0.0f, -gradient_height), mgl::Color(color.r, color.g, color.b, 0)); + gradient_points[1] = mgl::Vertex(card_bottom + mgl::vec2f(card_max_image_size.x, -gradient_height), mgl::Color(color.r, color.g, color.b, 0)); + gradient_points[2] = mgl::Vertex(card_bottom + mgl::vec2f(card_max_image_size.x, 0.0f), color); + gradient_points[3] = mgl::Vertex(card_bottom + mgl::vec2f(0.0f, 0.0f), color); + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); } } } float Body::get_item_height(BodyItem *item, float width, bool load_texture, bool include_embedded_item, bool merge_with_previous, int item_index) { const bool rendering_card_view = card_view && card_view_enabled; - sf::Vector2i content_size = get_item_thumbnail_size(item); + mgl::vec2i content_size = get_item_thumbnail_size(item); const bool show_thumbnail = draw_thumbnails && !item->thumbnail_url.empty() && !merge_with_previous; if(load_texture && show_thumbnail) { std::shared_ptr<ThumbnailData> item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, content_size); - content_size = clamp_to_size_x(content_size, sf::Vector2i(width - (rendering_card_view ? 0.0f : body_spacing[body_theme].image_padding_x * 2.0f), content_size.y)); + content_size = clamp_to_size_x(content_size, width - (rendering_card_view ? 0.0f : body_spacing[body_theme].image_padding_x * 2.0f)); - if(item_thumbnail && item_thumbnail->loading_state == LoadingState::FINISHED_LOADING && item_thumbnail->image->getSize().x > 0 && item_thumbnail->image->getSize().y > 0) { - if(!item_thumbnail->texture.loadFromImage(*item_thumbnail->image)) + if(item_thumbnail && item_thumbnail->loading_state == LoadingState::FINISHED_LOADING && item_thumbnail->image->get_size().x > 0 && item_thumbnail->image->get_size().y > 0) { + if(!item_thumbnail->texture.load_from_image(*item_thumbnail->image)) fprintf(stderr, "Warning: failed to load texture from image: %s\n", item->thumbnail_url.c_str()); - item_thumbnail->texture.setSmooth(true); //item_thumbnail->texture.generateMipmap(); item_thumbnail->image.reset(); item_thumbnail->loading_state = LoadingState::APPLIED_TO_TEXTURE; } - if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { - auto image_size = item_thumbnail->texture.getSize(); - sf::Vector2f image_size_f(image_size.x, image_size.y); - auto new_image_size = clamp_to_size(image_size_f, to_vec2f(content_size)); + if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.is_valid()) { + auto image_size = item_thumbnail->texture.get_size(); + mgl::vec2f image_size_f(image_size.x, image_size.y); + auto new_image_size = clamp_to_size(image_size_f, content_size.to_vec2f()); item->loaded_image_size = new_image_size; } else { if(item->loaded_image_size.y < 0.1f) - item->loaded_image_size = to_vec2f(content_size); + item->loaded_image_size = content_size.to_vec2f(); } } else if(item->thumbnail_size.x > 0) { if(!show_thumbnail) diff --git a/src/BodyItem.cpp b/src/BodyItem.cpp index 6523738..ebc3ead 100644 --- a/src/BodyItem.cpp +++ b/src/BodyItem.cpp @@ -45,7 +45,7 @@ namespace QuickMedia { post_number = other.post_number; userdata = other.userdata; loaded_height = 0.0f; - loaded_image_size = sf::Vector2f(0.0f, 0.0f); + loaded_image_size = mgl::vec2f(0.0f, 0.0f); loaded_content_height = 0.0f; embedded_item_status = other.embedded_item_status; if(other.embedded_item) { @@ -77,14 +77,13 @@ namespace QuickMedia { } void BodyItem::add_reaction(std::string text, void *userdata) { - sf::String str = sf::String::fromUtf8(text.begin(), text.end()); Reaction reaction; - reaction.text = std::make_unique<Text>(std::move(str), false, std::floor(get_config().body.reaction_font_size * get_config().scale * get_config().font_scale), 0.0f); + reaction.text = std::make_unique<Text>(std::move(text), false, std::floor(get_config().body.reaction_font_size * get_config().scale * get_config().font_scale), 0.0f); reaction.userdata = userdata; reactions.push_back(std::move(reaction)); } - void BodyItem::draw_list(Body *body, sf::RenderTarget &render_target) { + void BodyItem::draw_list(Body *body, mgl::Window &render_target) { } }
\ No newline at end of file diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp index 6df9c74..584d4a3 100644 --- a/src/DownloadUtils.cpp +++ b/src/DownloadUtils.cpp @@ -5,8 +5,8 @@ #include "../external/hash-library/sha256.h" #include <unistd.h> #include <limits.h> -#include <SFML/System/Clock.hpp> -#include <SFML/Window/Event.hpp> +#include <mglpp/system/Clock.hpp> +#include <mglpp/window/Event.hpp> #include <rapidjson/document.h> #include <rapidjson/filereadstream.h> @@ -86,7 +86,7 @@ namespace QuickMedia { DownloadResult download_head_to_string(const std::string &url, std::string &result, bool use_browser_useragent, bool fail_on_error) { result.clear(); - sf::Clock timer; + mgl::Clock timer; std::vector<const char*> args; args.insert(args.end(), { "curl", "-I", "-g", "-H", "Accept-Language: en-US,en;q=0.5", "-H", "Connection: keep-alive", "--compressed", "-s" }); if(fail_on_error) @@ -107,13 +107,13 @@ namespace QuickMedia { } if(exec_program(args.data(), accumulate_string, &result) != 0) return DownloadResult::NET_ERR; - fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), timer.getElapsedTime().asMilliseconds()); + fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), (int)(timer.get_elapsed_time_seconds() * 1000.0)); return DownloadResult::OK; } DownloadResult url_get_remote_name(const std::string &url, std::string &result, bool use_browser_useragent) { result.clear(); - sf::Clock timer; + mgl::Clock timer; std::vector<const char*> args; args.insert(args.end(), { "curl", "-I", "-g", "-H", "Accept-Language: en-US,en;q=0.5", "-H", "Connection: keep-alive", "--compressed", "-s" }); if(use_browser_useragent) { @@ -133,7 +133,7 @@ namespace QuickMedia { std::string header; if(exec_program(args.data(), accumulate_string, &header) != 0) return DownloadResult::NET_ERR; - fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), timer.getElapsedTime().asMilliseconds()); + fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), (int)(timer.get_elapsed_time_seconds() * 1000.0)); std::string content_disposition = header_extract_value(header, "content-disposition"); // TODO: after filename*= the encoding type will follow. We need to support other formats than utf-8 as well @@ -195,7 +195,7 @@ namespace QuickMedia { // TODO: Add timeout DownloadResult download_to_string(const std::string &url, std::string &result, const std::vector<CommandArg> &additional_args, bool use_browser_useragent, bool fail_on_error, bool cloudflare_bypass, std::vector<std::string> *headers, int download_limit) { result.clear(); - sf::Clock timer; + mgl::Clock timer; std::vector<const char*> args; args.insert(args.end(), { "curl", "-H", "Accept-Language: en-US,en;q=0.5", "-H", "Connection: keep-alive", "--compressed", "-g", "-s", "-L" }); if(fail_on_error) @@ -234,7 +234,7 @@ namespace QuickMedia { if(exec_program(args.data(), accumulate_string_with_header, &download_userdata) != 0) return DownloadResult::NET_ERR; - fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), timer.getElapsedTime().asMilliseconds()); + fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), (int)(timer.get_elapsed_time_seconds() * 1000.0)); return DownloadResult::OK; } @@ -307,7 +307,7 @@ namespace QuickMedia { // TODO: Add timeout DownloadResult download_to_json(const std::string &url, rapidjson::Document &result, const std::vector<CommandArg> &additional_args, bool use_browser_useragent, bool fail_on_error) { - sf::Clock timer; + mgl::Clock timer; std::vector<const char*> args; args.insert(args.end(), { "curl", "-H", "Accept-Language: en-US,en;q=0.5", "-H", "Connection: keep-alive", "--compressed", "-g", "-s", "-L" }); if(fail_on_error) @@ -350,7 +350,7 @@ namespace QuickMedia { fclose(file); wait_program(read_program.pid); - fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), timer.getElapsedTime().asMilliseconds()); + fprintf(stderr, "Download duration for %s: %d ms\n", url.c_str(), (int)(timer.get_elapsed_time_seconds() * 1000.0)); return parse_result.IsError() ? DownloadResult::ERR : DownloadResult::OK; } } diff --git a/src/Downloader.cpp b/src/Downloader.cpp index 820ca81..1bb3e34 100644 --- a/src/Downloader.cpp +++ b/src/Downloader.cpp @@ -2,7 +2,7 @@ #include "../include/Storage.hpp" #include "../include/NetUtils.hpp" #include "../include/Notification.hpp" -#include <SFML/System/Clock.hpp> +#include <mglpp/system/Clock.hpp> #include <unistd.h> #include <signal.h> @@ -346,8 +346,8 @@ namespace QuickMedia { } downloader_task = AsyncTask<void>([this]() { - sf::Clock timer; - const double sleep_time_millisec = 1; + mgl::Clock timer; + // TODO: Poll instead of sleep while(!program_is_dead_in_current_thread()) { if(youtube_video_media_proxy) youtube_video_media_proxy->update(); @@ -355,9 +355,9 @@ namespace QuickMedia { if(youtube_audio_media_proxy) youtube_audio_media_proxy->update(); - const int sleep_time = sleep_time_millisec - timer.restart().asMilliseconds(); - if(sleep_time > 0) - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); + const double sleep_left_sec = timer.restart(); + if(sleep_left_sec > 0.000001) + std::this_thread::sleep_for(std::chrono::milliseconds((long)(sleep_left_sec * 1000.0))); } }); diff --git a/src/Entry.cpp b/src/Entry.cpp index f0c02fd..590eade 100644 --- a/src/Entry.cpp +++ b/src/Entry.cpp @@ -3,9 +3,10 @@ #include "../include/Config.hpp" #include "../include/Utils.hpp" #include "../include/Theme.hpp" -#include <SFML/Graphics/RenderWindow.hpp> -#include <SFML/Graphics/RectangleShape.hpp> -#include <SFML/Window/Event.hpp> +#include <mglpp/system/FloatRect.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/window/Event.hpp> #include <math.h> namespace QuickMedia { @@ -13,29 +14,29 @@ namespace QuickMedia { static const float padding_vertical = std::floor(5.0f * get_config().scale); static const float background_margin_vertical = std::floor(0.0f * get_config().scale); - Entry::Entry(const std::string &placeholder_text, sf::Shader *rounded_rectangle_shader) : + Entry::Entry(const std::string &placeholder_text, mgl::Shader *rounded_rectangle_shader) : on_submit_callback(nullptr), draw_background(true), text("", false, std::floor(get_config().input.font_size * get_config().scale * get_config().font_scale), 0.0f), width(0.0f), - background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), - placeholder(placeholder_text, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(get_config().input.font_size * get_config().scale * get_config().font_scale)), + background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), + placeholder(placeholder_text, *FontLoader::get_font(FontLoader::FontType::LATIN, get_config().input.font_size * get_config().scale * get_config().font_scale)), mouse_left_inside(false) { text.setEditable(true); - placeholder.setFillColor(get_theme().placeholder_text_color); + placeholder.set_color(get_theme().placeholder_text_color); } - void Entry::process_event(sf::Event &event) { - if(is_touch_enabled() && event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) { - sf::FloatRect box(background.get_position(), background.get_size()); - if(box.contains(event.mouseButton.x, event.mouseButton.y)) + void Entry::process_event(mgl::Window &window, mgl::Event &event) { + if(is_touch_enabled() && event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { + mgl::FloatRect box(background.get_position(), background.get_size()); + if(box.contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) mouse_left_inside = true; else mouse_left_inside = false; - } else if(is_touch_enabled() && event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left) { - sf::FloatRect box(background.get_position(), background.get_size()); - if(mouse_left_inside && box.contains(event.mouseButton.x, event.mouseButton.y)) + } else if(is_touch_enabled() && event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Left) { + mgl::FloatRect box(background.get_position(), background.get_size()); + if(mouse_left_inside && box.contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) show_virtual_keyboard(); mouse_left_inside = false; } @@ -43,13 +44,11 @@ namespace QuickMedia { if(!text.isEditable()) return; - text.processEvent(event); + text.processEvent(window, event); - if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Enter && !event.key.shift) { + if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Enter && !event.key.shift) { if(on_submit_callback) { - auto u8 = text.getString().toUtf8(); - std::string *u8_str = (std::string*)&u8; - bool clear_text = on_submit_callback(*u8_str); + bool clear_text = on_submit_callback(text.getString()); if(clear_text) text.setString(""); } @@ -58,18 +57,18 @@ namespace QuickMedia { // TODO: Set the max number of visible lines and use glScissor to cut off the lines outsides // (and also split text into lines to not draw them at all once they are not inside the scissor box) - void Entry::draw(sf::RenderWindow &window) { - background.set_size(sf::Vector2f(width, get_height())); + void Entry::draw(mgl::Window &window) { + background.set_size(mgl::vec2f(width, get_height())); if(draw_background) background.draw(window); - if(text.getString().isEmpty() && !text.isEditable()) { + if(text.getString().empty() && !text.isEditable()) { window.draw(placeholder); - //sf::Vector2f placeholder_pos = placeholder.getPosition(); + //mgl::vec2f placeholder_pos = placeholder.get_position(); //const float caret_margin = 2.0f; //const float vspace = placeholder.getFont()->getLineSpacing(18); - //sf::RectangleShape caret_rect(sf::Vector2f(2.0f, floor(vspace - caret_margin * 2.0f))); - //caret_rect.setPosition(floor(placeholder_pos.x), floor(placeholder_pos.y + caret_margin)); + //mgl::Rectangle caret_rect(mgl::vec2f(2.0f, floor(vspace - caret_margin * 2.0f))); + //caret_rect.set_position(floor(placeholder_pos.x), floor(placeholder_pos.y + caret_margin)); //window.draw(caret_rect); } else { text.draw(window); @@ -84,8 +83,8 @@ namespace QuickMedia { text.setEditable(editable); } - void Entry::set_text(const std::string &new_text) { - text.setString(sf::String::fromUtf8(new_text.begin(), new_text.end())); + void Entry::set_text(std::string new_text) { + text.setString(std::move(new_text)); } void Entry::move_caret_to_end() { @@ -93,11 +92,11 @@ namespace QuickMedia { text.moveCaretToEnd(); } - void Entry::append_text(const std::string &str) { - text.appendText(sf::String::fromUtf8(str.begin(), str.end())); + void Entry::insert_text_at_caret_position(const std::string &str) { + text.insert_text_at_caret_position(str); } - void Entry::replace(size_t start_index, size_t length, const sf::String &insert_str) { + void Entry::replace(size_t start_index, size_t length, const std::string &insert_str) { text.replace(start_index, length, insert_str); } @@ -105,10 +104,10 @@ namespace QuickMedia { return text.getCaretIndex(); } - void Entry::set_position(const sf::Vector2f &pos) { + void Entry::set_position(const mgl::vec2f &pos) { background.set_position(pos); - text.setPosition(pos + sf::Vector2f(background_margin_horizontal, background_margin_vertical)); - placeholder.setPosition(pos + sf::Vector2f(background_margin_horizontal, background_margin_vertical + std::floor(5.0f * get_config().scale))); + text.set_position(pos + mgl::vec2f(background_margin_horizontal, background_margin_vertical)); + placeholder.set_position(pos + mgl::vec2f(background_margin_horizontal, background_margin_vertical + std::floor(-1.0f * get_config().scale))); } void Entry::set_max_width(float width) { @@ -125,7 +124,7 @@ namespace QuickMedia { return std::floor(text.getHeight() + background_margin_vertical * 2.0f + padding_vertical * 2.0f); } - const sf::String& Entry::get_text() const { + const std::string& Entry::get_text() const { return text.getString(); } }
\ No newline at end of file diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp index f1f9369..6f1c1ee 100644 --- a/src/FileAnalyzer.cpp +++ b/src/FileAnalyzer.cpp @@ -137,7 +137,7 @@ namespace QuickMedia { if(width > 0 && height > 0) { FileAnalyzer file_analyzer; - const bool success = file_analyzer.load_file(destination_path_tmp.data.c_str(), false) && create_thumbnail(destination_path_tmp, destination_path, sf::Vector2i(width, height), file_analyzer.get_content_type(), false); + const bool success = file_analyzer.load_file(destination_path_tmp.data.c_str(), false) && create_thumbnail(destination_path_tmp, destination_path, mgl::vec2i(width, height), file_analyzer.get_content_type(), false); remove(destination_path_tmp.data.c_str()); return success; } else { diff --git a/src/ImageViewer.cpp b/src/ImageViewer.cpp index 9c6abf7..731099f 100644 --- a/src/ImageViewer.cpp +++ b/src/ImageViewer.cpp @@ -1,34 +1,37 @@ #include "../include/ImageViewer.hpp" #include "../include/Notification.hpp" #include "../include/Storage.hpp" -#include "../include/SfmlFixes.hpp" #include "../include/ResourceLoader.hpp" #include "../include/Scale.hpp" #include "../include/Config.hpp" +#include <mglpp/system/FloatRect.hpp> #include <cmath> #include <malloc.h> -#include <SFML/Window/Event.hpp> -#include <SFML/Graphics/RenderWindow.hpp> -#include <SFML/Graphics/RectangleShape.hpp> +#include <mglpp/window/Event.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/graphics/Image.hpp> namespace QuickMedia { - static sf::Vector2<double> get_no_image_size_scaled(sf::Vector2<double> window_size, bool fit_image_to_window) { - sf::Vector2<double> no_image_page_size(720.0, 1280.0); - sf::Vector2f no_image_page_size_f(no_image_page_size.x, no_image_page_size.y); - sf::Vector2f content_size(window_size.x, window_size.y); + static const int page_text_character_size = 14 * get_config().scale * get_config().font_scale; - sf::Vector2f image_scale; + static mgl::vec2d get_no_image_size_scaled(mgl::vec2d window_size, bool fit_image_to_window) { + mgl::vec2d no_image_page_size(720.0, 1280.0); + mgl::vec2f no_image_page_size_f(no_image_page_size.x, no_image_page_size.y); + mgl::vec2f content_size(window_size.x, window_size.y); + + mgl::vec2f image_scale; if(fit_image_to_window) - image_scale = get_ratio(no_image_page_size_f, wrap_to_size_x(no_image_page_size_f, content_size)); + image_scale = get_ratio(no_image_page_size_f, wrap_to_size_x(no_image_page_size_f, content_size.x)); else - image_scale = get_ratio(no_image_page_size_f, clamp_to_size_x(no_image_page_size_f, content_size)); + image_scale = get_ratio(no_image_page_size_f, clamp_to_size_x(no_image_page_size_f, content_size.x)); no_image_page_size.x *= image_scale.x; no_image_page_size.y *= image_scale.y; return no_image_page_size; } - ImageViewer::ImageViewer(sf::RenderWindow *window, int num_pages, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir, bool *fit_image_to_window) : + ImageViewer::ImageViewer(mgl::Window *window, int num_pages, const std::string &content_title, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir, bool *fit_image_to_window) : window(window), current_page(current_page), num_pages(num_pages), @@ -36,8 +39,7 @@ namespace QuickMedia { chapter_title(chapter_title), chapter_cache_dir(chapter_cache_dir), focused_page(current_page), - font(FontLoader::get_font(FontLoader::FontType::LATIN)), - page_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), 14 * get_config().scale * get_config().font_scale), + page_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, page_text_character_size)), fit_image_to_window(fit_image_to_window) { current_page = std::min(current_page, num_pages); @@ -47,29 +49,30 @@ namespace QuickMedia { image_data[i] = nullptr; page_size[i].loaded = false; } - page_text.setFillColor(sf::Color::White); + page_text.set_color(mgl::Color(255, 255, 255, 255)); - has_default_cursor = default_cursor.loadFromSystem(sf::Cursor::Arrow); - has_size_vertical_cursor = size_vertical_cursor.loadFromSystem(sf::Cursor::SizeVertical); + // TODO: Fix + //has_default_cursor = default_cursor.loadFromSystem(sf::Cursor::Arrow); + //has_size_vertical_cursor = size_vertical_cursor.loadFromSystem(sf::Cursor::SizeVertical); } ImageViewer::~ImageViewer() { - if(has_default_cursor) - window->setMouseCursor(default_cursor); + // TODO: Fix + //if(has_default_cursor) + // window->setMouseCursor(default_cursor); if(image_loader_thread.joinable()) image_loader_thread.join(); } void ImageViewer::load_image_async(const Path &path, std::shared_ptr<ImageData> image_data) { image_data->image_status = ImageStatus::LOADING; - //image_data->texture.setSmooth(true); assert(!loading_image); loading_image = true; if(image_loader_thread.joinable()) image_loader_thread.join(); image_loader_thread = std::thread([this, image_data, path]() mutable { - auto image = std::make_unique<sf::Image>(); - if(load_image_from_file(*image, path.data)) { + auto image = std::make_unique<mgl::Image>(); + if(image->load_from_file(path.data.c_str())) { image_data->image = std::move(image); image_data->image_status = ImageStatus::LOADED; } else { @@ -79,13 +82,13 @@ namespace QuickMedia { }); } - bool ImageViewer::render_page(sf::RenderWindow &window, int page, double offset_y) { + bool ImageViewer::render_page(mgl::Window &window, int page, double offset_y) { if(page < 0 || page >= (int)image_data.size()) return false; std::shared_ptr<ImageData> &page_image_data = image_data[page]; - const sf::Vector2<double> image_size = get_page_size(page); - sf::Vector2<double> render_pos(std::floor(window_size.x * 0.5 - image_size.x * 0.5), scroll + offset_y); + const mgl::vec2d image_size = get_page_size(page); + mgl::vec2d render_pos(std::floor(window_size.x * 0.5 - image_size.x * 0.5), scroll + offset_y); if(render_pos.y + image_size.y <= 0.0 || render_pos.y >= window_size.y) { if(page_image_data) page_image_data->visible_on_screen = false; @@ -106,18 +109,18 @@ namespace QuickMedia { page_image_data->visible_on_screen = true; if(page_image_data->image_status == ImageStatus::APPLIED_TO_TEXTURE) { - auto texture_size = page_image_data->sprite.getTexture()->getSize(); - sf::Vector2f texture_size_f(texture_size.x, texture_size.y); - sf::Vector2f content_size(window_size.x, window_size.y); + auto texture_size = page_image_data->sprite.get_texture()->get_size(); + mgl::vec2f texture_size_f(texture_size.x, texture_size.y); + mgl::vec2f content_size(window_size.x, window_size.y); - sf::Vector2f image_scale; + mgl::vec2f image_scale; if(*fit_image_to_window) - image_scale = get_ratio(texture_size_f, wrap_to_size_x(texture_size_f, content_size)); + image_scale = get_ratio(texture_size_f, wrap_to_size_x(texture_size_f, content_size.x)); else - image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, content_size)); + image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, content_size.x)); - page_image_data->sprite.setScale(image_scale); - page_image_data->sprite.setPosition(render_pos.x, render_pos.y); + page_image_data->sprite.set_scale(image_scale); + page_image_data->sprite.set_position(mgl::vec2f(render_pos.x, render_pos.y)); window.draw(page_image_data->sprite); } else { std::string page_str = std::to_string(1 + page); @@ -135,39 +138,39 @@ namespace QuickMedia { msg = "Failed to load image for page " + page_str; } - sf::Text error_message(std::move(msg), *font, 30 * get_config().scale * get_config().font_scale); - auto text_bounds = error_message.getLocalBounds(); - error_message.setFillColor(sf::Color::Black); - sf::Vector2<double> render_pos_text(std::floor(window_size.x * 0.5 - text_bounds.width * 0.5), image_size.y * 0.5 - text_bounds.height * 0.5 + scroll + offset_y); + mgl::Text error_message(std::move(msg), *FontLoader::get_font(FontLoader::FontType::LATIN, 30 * get_config().scale * get_config().font_scale)); + auto text_bounds = error_message.get_bounds(); + error_message.set_color(mgl::Color(0, 0, 0, 255)); + mgl::vec2d render_pos_text(std::floor(window_size.x * 0.5 - text_bounds.size.x * 0.5), image_size.y * 0.5 - text_bounds.size.y * 0.5 + scroll + offset_y); if(!scrolling) render_pos_text.y = std::floor(render_pos_text.y); - sf::RectangleShape background(sf::Vector2f(image_size.x, image_size.y)); - background.setFillColor(sf::Color::White); - background.setPosition(render_pos.x, render_pos.y); + mgl::Rectangle background(mgl::vec2f(image_size.x, image_size.y)); + background.set_color(mgl::Color(255, 255, 255, 255)); + background.set_position(mgl::vec2f(render_pos.x, render_pos.y)); window.draw(background); - error_message.setPosition(render_pos_text.x, render_pos_text.y); + error_message.set_position(mgl::vec2f(render_pos_text.x, render_pos_text.y)); window.draw(error_message); } } else { std::string page_str = std::to_string(1 + page); - sf::Text error_message("Downloading page " + page_str, *font, 30 * get_config().scale * get_config().font_scale); - auto text_bounds = error_message.getLocalBounds(); - error_message.setFillColor(sf::Color::Black); - sf::Vector2<double> render_pos_text(std::floor(window_size.x * 0.5 - text_bounds.width * 0.5), image_size.y * 0.5 - text_bounds.height * 0.5 + scroll + offset_y); + mgl::Text error_message("Downloading page " + page_str, *FontLoader::get_font(FontLoader::FontType::LATIN, 30 * get_config().scale * get_config().font_scale)); + auto text_bounds = error_message.get_bounds(); + error_message.set_color(mgl::Color(0, 0, 0, 255)); + mgl::vec2d render_pos_text(std::floor(window_size.x * 0.5 - text_bounds.size.x * 0.5), image_size.y * 0.5 - text_bounds.size.y * 0.5 + scroll + offset_y); if(!scrolling) render_pos_text.y = std::floor(render_pos_text.y); - sf::RectangleShape background(sf::Vector2f(image_size.x, image_size.y)); - background.setFillColor(sf::Color::White); - background.setPosition(render_pos.x, render_pos.y); + mgl::Rectangle background(mgl::vec2f(image_size.x, image_size.y)); + background.set_color(mgl::Color(255, 255, 255, 255)); + background.set_position(mgl::vec2f(render_pos.x, render_pos.y)); window.draw(background); - error_message.setPosition(render_pos_text.x, render_pos_text.y); + error_message.set_position(mgl::vec2f(render_pos_text.x, render_pos_text.y)); window.draw(error_message); Path image_path = chapter_cache_dir; @@ -181,7 +184,6 @@ namespace QuickMedia { page_image_data->visible_on_screen = true; page_image_data->image_status = ImageStatus::WAITING; - //page_image_data->texture.setSmooth(true); } } @@ -193,73 +195,69 @@ namespace QuickMedia { } ImageViewerAction ImageViewer::draw() { - const double frame_delta = frame_timer.restart().asSeconds(); + const double frame_delta = frame_timer.restart(); const double scroll_speed_key_input = 50.0; const double scroll_speed_mouse_wheel = 600.0; const double scroll_speed_autoscroll = 10.0; const double scroll_deaccel = 0.96; if(!window_size_set) { - auto window_size_i = window->getSize(); + auto window_size_i = window->get_size(); window_size.x = window_size_i.x; window_size.y = window_size_i.y; window_size_set = true; for(int i = 0; i < current_page; ++i) { - const sf::Vector2<double> size = get_page_size(i); + const mgl::vec2d size = get_page_size(i); scroll -= size.y; page_size[i].prev_size = size; } } // TODO: Only redraw when scrolling and when image has finished downloading - sf::Event event; - while(window->pollEvent(event)) { - if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Q && event.key.control) + mgl::Event event; + while(window->poll_event(event)) { + if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Q && event.key.control) window->close(); } - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window->setView(sf::View(visible_area)); - //redraw = true; - } else if(event.type == sf::Event::GainedFocus) { - //redraw = true; - } else if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) + } else if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Up || (event.key.control && event.key.code == mgl::Keyboard::K)) up_pressed = true; - if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) + if(event.key.code == mgl::Keyboard::Down || (event.key.control && event.key.code == mgl::Keyboard::J)) down_pressed = true; - if(event.key.code == sf::Keyboard::Escape) + if(event.key.code == mgl::Keyboard::Escape) return ImageViewerAction::RETURN; - if(event.key.code == sf::Keyboard::I) + if(event.key.code == mgl::Keyboard::I) return ImageViewerAction::SWITCH_TO_SINGLE_IMAGE_MODE; - if(event.key.code == sf::Keyboard::F) + if(event.key.code == mgl::Keyboard::F) *fit_image_to_window = !*fit_image_to_window; - } else if(event.type == sf::Event::KeyReleased) { - if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) + } else if(event.type == mgl::Event::KeyReleased) { + if(event.key.code == mgl::Keyboard::Up || (event.key.control && event.key.code == mgl::Keyboard::K)) up_pressed = false; - if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) + if(event.key.code == mgl::Keyboard::Down || (event.key.control && event.key.code == mgl::Keyboard::J)) down_pressed = false; - } else if(event.type == sf::Event::MouseWheelScrolled && event.mouseWheelScroll.wheel == sf::Mouse::VerticalWheel) { - scroll_speed += scroll_speed_mouse_wheel * event.mouseWheelScroll.delta * frame_delta; - } else if(event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Button::Middle) { + } else if(event.type == mgl::Event::MouseWheelScrolled/* && event.mouse_wheel_scroll.wheel == mgl::Mouse::VerticalWheel*/) { + scroll_speed += scroll_speed_mouse_wheel * event.mouse_wheel_scroll.delta * frame_delta; + } else if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Button::Middle) { middle_mouse_scrolling = true; - autoscroll_start_y = sf::Mouse::getPosition(*window).y; - if(has_size_vertical_cursor) - window->setMouseCursor(size_vertical_cursor); - - } else if(event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Button::Middle) { + autoscroll_start_y = window->get_mouse_position().y; + // TODO: Fix + //if(has_size_vertical_cursor) + // window->setMouseCursor(size_vertical_cursor); + } else if(event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Button::Middle) { middle_mouse_scrolling = false; scroll_speed = 0.0; - if(has_default_cursor) - window->setMouseCursor(default_cursor); + // TODO: Fix + //if(has_default_cursor) + // window->setMouseCursor(default_cursor); } } @@ -269,7 +267,7 @@ namespace QuickMedia { scroll_speed -= scroll_speed_key_input * frame_delta; if(middle_mouse_scrolling) { - double distance_to_start_y = (double)sf::Mouse::getPosition(*window).y - autoscroll_start_y; + double distance_to_start_y = (double)window->get_mouse_position().y - autoscroll_start_y; double dist_abs = std::abs(distance_to_start_y); dist_abs -= 20.0; if(dist_abs < 0.0) @@ -304,14 +302,13 @@ namespace QuickMedia { int page_i = 0; for(auto &page_data : image_data) { if(page_data) { - if(page_data->image_status == ImageStatus::LOADED && page_data->image->getSize().x > 0 && page_data->image->getSize().y > 0) { - if(page_data->texture.loadFromImage(*page_data->image)) { - page_data->texture.setSmooth(true); + if(page_data->image_status == ImageStatus::LOADED && page_data->image->get_size().x > 0 && page_data->image->get_size().y > 0) { + if(page_data->texture.load_from_image(*page_data->image)) { //page_data->texture.generateMipmap(); page_data->image_status = ImageStatus::APPLIED_TO_TEXTURE; - page_data->sprite.setTexture(page_data->texture, true); - sf::Vector2u texture_size = image_data[page_i]->texture.getSize(); - page_size[page_i].size = sf::Vector2<double>(texture_size.x, texture_size.y); + page_data->sprite.set_texture(&page_data->texture); + mgl::vec2i texture_size = image_data[page_i]->texture.get_size(); + page_size[page_i].size = mgl::vec2d(texture_size.x, texture_size.y); page_size[page_i].loaded = true; } else { page_data->image_status = ImageStatus::FAILED_TO_LOAD; @@ -326,7 +323,7 @@ namespace QuickMedia { double page_offset = 0.0; for(int i = 0; i < num_pages; ++i) { - const sf::Vector2<double> current_page_size = get_page_size(i); + const mgl::vec2d current_page_size = get_page_size(i); const double scroll_diff = current_page_size.y - page_size[i].prev_size.y; page_size[i].prev_size = current_page_size; @@ -349,19 +346,19 @@ namespace QuickMedia { if(focused_page != prev_focused_page) { prev_focused_page = focused_page; - page_text.setString(content_title + " | " + chapter_title + " | Page " + std::to_string(1 + focused_page) + "/" + std::to_string(num_pages)); + page_text.set_string(content_title + " | " + chapter_title + " | Page " + std::to_string(1 + focused_page) + "/" + std::to_string(num_pages)); } - const float font_height = page_text.getCharacterSize() + 8.0f; + const float font_height = page_text_character_size + 8.0f; const float background_height = font_height + 6.0f; - sf::RectangleShape page_text_background(sf::Vector2f(window_size.x, background_height)); - page_text_background.setFillColor(sf::Color(0, 0, 0, 150)); - page_text_background.setPosition(0.0f, window_size.y - background_height); + mgl::Rectangle page_text_background(mgl::vec2f(window_size.x, background_height)); + page_text_background.set_color(mgl::Color(0, 0, 0, 150)); + page_text_background.set_position(mgl::vec2f(0.0f, window_size.y - background_height)); window->draw(page_text_background); - auto page_text_bounds = page_text.getLocalBounds(); - page_text.setPosition(std::floor(window_size.x * 0.5f - page_text_bounds.width * 0.5f), std::floor(window_size.y - background_height * 0.5f - font_height * 0.5f)); + auto page_text_bounds = page_text.get_bounds(); + page_text.set_position(mgl::vec2f(std::floor(window_size.x * 0.5f - page_text_bounds.size.x * 0.5f), std::floor(window_size.y - background_height * 0.5f - font_height * 0.6f))); window->draw(page_text); // Free pages that are not visible on the screen @@ -385,30 +382,30 @@ namespace QuickMedia { return 1 + focused_page; } - sf::Vector2<double> ImageViewer::get_page_size(int page) { - sf::Vector2<double> no_image_page_size = get_no_image_size_scaled(window_size, *fit_image_to_window); + mgl::vec2d ImageViewer::get_page_size(int page) { + mgl::vec2d no_image_page_size = get_no_image_size_scaled(window_size, *fit_image_to_window); if(page < 0 || page >= (int)image_data.size()) return no_image_page_size; if(page_size[page].loaded) { - sf::Vector2f texture_size_f(page_size[page].size.x, page_size[page].size.y); - sf::Vector2f content_size(window_size.x, window_size.y); + mgl::vec2f texture_size_f(page_size[page].size.x, page_size[page].size.y); + mgl::vec2f content_size(window_size.x, window_size.y); - sf::Vector2f image_scale; + mgl::vec2f image_scale; if(*fit_image_to_window) - image_scale = get_ratio(texture_size_f, wrap_to_size_x(texture_size_f, content_size)); + image_scale = get_ratio(texture_size_f, wrap_to_size_x(texture_size_f, content_size.x)); else - image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, content_size)); + image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, content_size.x)); - return sf::Vector2<double>(page_size[page].size.x * image_scale.x, page_size[page].size.y * image_scale.y); + return mgl::vec2d(page_size[page].size.x * image_scale.x, page_size[page].size.y * image_scale.y); } if(!image_data[page] || image_data[page]->image_status != ImageStatus::APPLIED_TO_TEXTURE) return no_image_page_size; // Do not scale here, because this will be used to set page_size[page].size - sf::Vector2u texture_size = image_data[page]->texture.getSize(); - return sf::Vector2<double>(texture_size.x, texture_size.y); + mgl::vec2i texture_size = image_data[page]->texture.get_size(); + return mgl::vec2d(texture_size.x, texture_size.y); } }
\ No newline at end of file diff --git a/src/NetUtils.cpp b/src/NetUtils.cpp index 4f5fce8..2dab2b8 100644 --- a/src/NetUtils.cpp +++ b/src/NetUtils.cpp @@ -273,51 +273,6 @@ namespace QuickMedia { return strings; } - static size_t is_start_of_utf8_codepoint(uint8_t c) { - if((c & 0x80) == 0) - return true; - else if((c & 0xE0) == 0xC0) - return true; - else if((c & 0xF0) == 0xE0) - return true; - else if((c & 0xF8) == 0xF0) - return true; - else - return false; - } - - void convert_utf8_to_utf32_ranges(const std::string &str, std::vector<Range> &ranges) { - if(ranges.empty()) - return; - - size_t ranges_index = 0; - size_t prev_range_offset = 0; - size_t num_codepoints = 0; - bool in_range = false; - - for(size_t i = 0; i < str.size(); ++i) { - if(ranges_index > 0 && in_range) { - ++prev_range_offset; - if(prev_range_offset == ranges[ranges_index - 1].length) { - ranges[ranges_index - 1].length = num_codepoints - ranges[ranges_index - 1].start; - prev_range_offset = 0; - in_range = false; - } - } - - if(i == ranges[ranges_index].start) { - in_range = true; - ranges[ranges_index].start = num_codepoints; - ++ranges_index; - if(ranges_index == ranges.size()) - break; - } - - if(is_start_of_utf8_codepoint(*(uint8_t*)&str[i])) - ++num_codepoints; - } - } - std::string header_extract_value(const std::string &header, const std::string &type) { std::string result; string_split(header, '\n', [&type, &result](const char *str, size_t size) { diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 69252fa..1a7f67a 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -29,7 +29,6 @@ #include "../external/cppcodec/base64_url.hpp" #include "../include/Entry.hpp" #include "../include/NetUtils.hpp" -#include "../include/SfmlFixes.hpp" #include "../include/ResourceLoader.hpp" #include "../include/Config.hpp" #include "../include/Tabs.hpp" @@ -38,6 +37,7 @@ #include "../include/Downloader.hpp" #include "../include/Storage.hpp" #include "../include/AsyncImageLoader.hpp" +#include <mglpp/system/FloatRect.hpp> #include "../plugins/youtube/YoutubeMediaProxy.hpp" #include "../include/gui/Button.hpp" #include "../external/hash-library/sha256.h" @@ -51,19 +51,22 @@ #include <libgen.h> #include <limits.h> -#include <SFML/Graphics/RectangleShape.hpp> -#include <SFML/Window/Clipboard.hpp> -#include <SFML/Graphics/Sprite.hpp> -#include <SFML/Graphics/Text.hpp> -#include <SFML/Window/Event.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/graphics/Sprite.hpp> +#include <mglpp/graphics/Text.hpp> +#include <mglpp/window/Event.hpp> +extern "C" { +#include <mgl/mgl.h> +} #include <json/reader.h> #include <json/writer.h> #include <X11/keysym.h> #include <X11/extensions/Xrandr.h> +#include <X11/Xatom.h> static int FPS_IDLE; static const double IDLE_TIMEOUT_SEC = 2.0; -static const sf::Vector2i AVATAR_THUMBNAIL_SIZE(std::floor(32), std::floor(32)); +static const mgl::vec2i AVATAR_THUMBNAIL_SIZE(std::floor(32), std::floor(32)); static const float more_items_height = 2.0f; static const std::pair<const char*, const char*> valid_plugins[] = { @@ -181,12 +184,12 @@ static void get_screen_resolution(Display *display, int *width, int *height) { *height = DefaultScreenOfDisplay(display)->height; } -static sf::Color interpolate_colors(sf::Color source, sf::Color target, double progress) { +static mgl::Color interpolate_colors(mgl::Color source, mgl::Color target, double progress) { int diff_r = (int)target.r - (int)source.r; int diff_g = (int)target.g - (int)source.g; int diff_b = (int)target.b - (int)source.b; int diff_a = (int)target.a - (int)source.a; - return sf::Color( + return mgl::Color( source.r + diff_r * progress, source.g + diff_g * progress, source.b + diff_b * progress, @@ -505,6 +508,7 @@ namespace QuickMedia { }; no_video = force_no_video; + mgl_init(); init(parent_window, program_path); if(strcmp(plugin_name, "download") == 0) { @@ -555,20 +559,31 @@ namespace QuickMedia { return exit_code; } - static sf::Vector2i get_focused_monitor_center(Display *disp, sf::Vector2i &monitor_size) { + // TODO: Move to mgl + static mgl::vec2i get_global_mouse_position(Display *display) { + Window dummy_w; + int dummy_i; + unsigned int dummy_u; + + mgl::vec2i mouse_pos; + XQueryPointer(display, DefaultRootWindow(display), &dummy_w, &dummy_w, &mouse_pos.x, &mouse_pos.y, &dummy_i, &dummy_i, &dummy_u); + return mouse_pos; + } + + static mgl::vec2i get_focused_monitor_center(Display *disp, mgl::vec2i &monitor_size) { int screen = DefaultScreen(disp); monitor_size.x = DisplayWidth(disp, screen); monitor_size.y = DisplayWidth(disp, screen); int screen_center_x = monitor_size.x / 2; int screen_center_y = monitor_size.y / 2; - sf::Vector2i focused_monitor_center(screen_center_x, screen_center_y); - auto mouse_pos = sf::Mouse::getPosition(); + mgl::vec2i focused_monitor_center(screen_center_x, screen_center_y); + mgl::vec2i mouse_pos = get_global_mouse_position(disp); for_each_active_monitor_output(disp, [&focused_monitor_center, mouse_pos, &monitor_size](const XRRCrtcInfo *crtc_info, const XRRModeInfo*){ - if(sf::Rect<int>(crtc_info->x, crtc_info->y, crtc_info->width, crtc_info->height).contains(mouse_pos)) { + if(mgl::Rect<int>(mgl::vec2i(crtc_info->x, crtc_info->y), mgl::vec2i(crtc_info->width, crtc_info->height)).contains(mouse_pos)) { monitor_size.x = crtc_info->width; monitor_size.y = crtc_info->height; - focused_monitor_center = sf::Vector2i(crtc_info->x + crtc_info->width/2, crtc_info->y + crtc_info->height/2); + focused_monitor_center = mgl::vec2i(crtc_info->x + crtc_info->width/2, crtc_info->y + crtc_info->height/2); } }); @@ -585,51 +600,26 @@ namespace QuickMedia { XSetErrorHandler(x_error_handler); XSetIOErrorHandler(x_io_error_handler); - wm_delete_window_atom = XInternAtom(disp, "WM_DELETE_WINDOW", False); - net_wm_ping_atom = XInternAtom(disp, "_NET_WM_PING", True); - - int screen = DefaultScreen(disp); - sf::Vector2i monitor_size; - sf::Vector2i focused_monitor_center = get_focused_monitor_center(disp, monitor_size); + mgl::vec2i monitor_size; + mgl::vec2i focused_monitor_center = get_focused_monitor_center(disp, monitor_size); if(strcmp(plugin_name, "download") == 0) { window_size.x = std::min(900, monitor_size.x); window_size.y = std::min(900, monitor_size.y); } - x11_window = XCreateWindow(disp, parent_window ? parent_window : DefaultRootWindow(disp), - focused_monitor_center.x - window_size.x * 0.5f, focused_monitor_center.y - window_size.y * 0.5f, window_size.x, window_size.y, 0, - DefaultDepth(disp, screen), - InputOutput, - DefaultVisual(disp, screen), - 0, nullptr); - if(!x11_window) { - show_notification("QuickMedia", "Failed to create window", Urgency::CRITICAL); - abort(); - } - + mgl::Window::CreateParams window_create_params; + window_create_params.position = { focused_monitor_center.x - window_size.x / 2, focused_monitor_center.y - window_size.y / 2 }; + window_create_params.size = window_size; if(strcmp(plugin_name, "download") == 0) { - XSizeHints *size_hints = XAllocSizeHints(); - if(size_hints) { - size_hints->width = window_size.x; - size_hints->min_width = window_size.x; - size_hints->max_width = window_size.x; - - size_hints->height = window_size.y; - size_hints->min_height = window_size.y; - size_hints->max_height = window_size.y; - size_hints->flags = PSize | PMinSize | PMaxSize; - - XSetWMNormalHints(disp, x11_window, size_hints); - XFree(size_hints); - } + window_create_params.min_size = window_size; + window_create_params.max_size = window_size; + } + window_create_params.parent_window = parent_window; + if(!window.create("QuickMedia", std::move(window_create_params))) { + show_notification("QuickMedia", "Failed to create opengl window", Urgency::CRITICAL); + abort(); } - - XStoreName(disp, x11_window, "QuickMedia"); - XMapWindow(disp, x11_window); - XFlush(disp); - - window.create(x11_window); if(!program_path.empty() && program_path.back() != '/') program_path += '/'; @@ -641,39 +631,39 @@ namespace QuickMedia { set_resource_loader_root_path(resources_root.c_str()); set_use_system_fonts(get_config().use_system_fonts); + init_body_themes(); if(!is_touch_enabled()) { - if(!circle_mask_shader.loadFromFile(resources_root + "shaders/circle_mask.glsl", sf::Shader::Type::Fragment)) { + if(!circle_mask_shader.load_from_file((resources_root + "shaders/circle_mask.glsl").c_str(), mgl::Shader::Type::Fragment)) { show_notification("QuickMedia", "Failed to load " + resources_root + "/shaders/circle_mask.glsl", Urgency::CRITICAL); abort(); } if(get_theme().drop_shadow) { - if(!rounded_rectangle_shader.loadFromFile(resources_root + "shaders/rounded_rectangle.glsl", sf::Shader::Type::Fragment)) { + if(!rounded_rectangle_shader.load_from_file((resources_root + "shaders/rounded_rectangle.glsl").c_str(), mgl::Shader::Type::Fragment)) { show_notification("QuickMedia", "Failed to load " + resources_root + "/shaders/rounded_rectangle.glsl", Urgency::CRITICAL); abort(); } } else { - if(!rounded_rectangle_shader.loadFromFile(resources_root + "shaders/rounded_rectangle_no_shadow.glsl", sf::Shader::Type::Fragment)) { + if(!rounded_rectangle_shader.load_from_file((resources_root + "shaders/rounded_rectangle_no_shadow.glsl").c_str(), mgl::Shader::Type::Fragment)) { show_notification("QuickMedia", "Failed to load " + resources_root + "/shaders/rounded_rectangle_no_shadow.glsl", Urgency::CRITICAL); abort(); } } - if(!rounded_rectangle_mask_shader.loadFromFile(resources_root + "shaders/rounded_rectangle_mask.glsl", sf::Shader::Type::Fragment)) { + if(!rounded_rectangle_mask_shader.load_from_file((resources_root + "shaders/rounded_rectangle_mask.glsl").c_str(), mgl::Shader::Type::Fragment)) { show_notification("QuickMedia", "Failed to load " + resources_root + "/shaders/rounded_rectangle_mask.glsl", Urgency::CRITICAL); abort(); } } - if(!loading_icon.loadFromFile(resources_root + "images/loading_icon.png")) { + if(!loading_icon.load_from_file((resources_root + "images/loading_icon.png").c_str())) { show_notification("QuickMedia", "Failed to load " + resources_root + "/images/loading_icon.png", Urgency::CRITICAL); abort(); } - loading_icon.setSmooth(true); - load_sprite.setTexture(loading_icon, true); - sf::Vector2u loading_icon_size = loading_icon.getSize(); - load_sprite.setOrigin(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f); + load_sprite.set_texture(&loading_icon); + mgl::vec2i loading_icon_size = loading_icon.get_size(); + load_sprite.set_origin(mgl::vec2f(loading_icon_size.x * 0.5f, loading_icon_size.y * 0.5f)); struct sigaction action; action.sa_handler = sigpipe_handler; @@ -684,9 +674,8 @@ namespace QuickMedia { XSetErrorHandler(x_error_handler); XSetIOErrorHandler(x_io_error_handler); - window.setVerticalSyncEnabled(true); monitor_hz = get_monitor_max_hz(disp); - window.setFramerateLimit(monitor_hz); + window.set_framerate_limit(monitor_hz); idle = false; fprintf(stderr, "Monitor hz: %d\n", monitor_hz); @@ -717,7 +706,7 @@ namespace QuickMedia { show_room_side_panel = true; main_thread_id = std::this_thread::get_id(); - auto window_size_u = window.getSize(); + auto window_size_u = window.get_size(); window_size.x = window_size_u.x; window_size.y = window_size_u.y; } @@ -1038,7 +1027,7 @@ namespace QuickMedia { if(!plugin_name || plugin_name[0] == '\0') return; - window.setTitle("QuickMedia - " + std::string(plugin_name)); + window.set_title(("QuickMedia - " + std::string(plugin_name)).c_str()); no_video = force_no_video; if(strcmp(plugin_name, "youtube-audio") == 0) { @@ -1051,12 +1040,12 @@ namespace QuickMedia { if(plugin_logo_name) plugin_logo_path = resources_root + "images/" + plugin_logo_name; - plugin_logo = sf::Texture(); + plugin_logo = mgl::Texture(); if(!plugin_logo_path.empty()) { - if(!plugin_logo.loadFromFile(plugin_logo_path)) + if(!plugin_logo.load_from_file(plugin_logo_path.c_str())) fprintf(stderr, "Failed to load plugin logo, path: %s\n", plugin_logo_path.c_str()); - plugin_logo.generateMipmap(); - plugin_logo.setSmooth(true); + // TODO: Fix + //plugin_logo.generateMipmap(); } if(strcmp(plugin_name, "launcher") == 0) { @@ -1253,13 +1242,13 @@ namespace QuickMedia { } } else if(strcmp(plugin_name, "pornhub") == 0) { check_youtube_dl_installed(plugin_name); - auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.pornhub.com/", sf::Vector2i(320/1.5f, 180/1.5f), false); + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.pornhub.com/", mgl::vec2i(320/1.5f, 180/1.5f), false); add_pornhub_handlers(search_page.get()); tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)}); use_youtube_dl = true; } else if(strcmp(plugin_name, "spankbang") == 0) { check_youtube_dl_installed(plugin_name); - auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://spankbang.com/", sf::Vector2i(500/2.5f, 281/2.5f), true); + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://spankbang.com/", mgl::vec2i(500/2.5f, 281/2.5f), true); add_spankbang_handlers(search_page.get()); tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)}); use_youtube_dl = true; @@ -1269,13 +1258,13 @@ namespace QuickMedia { std::vector<CommandArg> extra_commands = { { "-H", "Cookie: last_views=%5B%2236247565-" + std::to_string(time(nullptr)) + "%22%5D" } }; - auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.xvideos.com/", sf::Vector2i(352/1.5f, 198/1.5f), false, std::move(extra_commands)); + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://www.xvideos.com/", mgl::vec2i(352/1.5f, 198/1.5f), false, std::move(extra_commands)); add_xvideos_handlers(search_page.get()); tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)}); use_youtube_dl = true; } else if(strcmp(plugin_name, "xhamster") == 0) { check_youtube_dl_installed(plugin_name); - auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://xhamster.com/", sf::Vector2i(240, 135), false); + auto search_page = std::make_unique<MediaGenericSearchPage>(this, "https://xhamster.com/", mgl::vec2i(240, 135), false); add_xhamster_handlers(search_page.get()); tabs.push_back(Tab{create_body(false, true), std::move(search_page), create_search_bar("Search...", 500)}); use_youtube_dl = true; @@ -1296,53 +1285,44 @@ namespace QuickMedia { } } - void Program::common_event_handler(sf::Event &event) { - if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Q && event.key.control) + void Program::common_event_handler(mgl::Event &event) { + if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Q && event.key.control) window.close(); } } void Program::handle_x11_events() { - window_closed = false; - - while(XCheckTypedWindowEvent(disp, x11_window, ClientMessage, &xev)) { - if(net_wm_ping_atom && xev.xclient.format == 32 && (Atom)xev.xclient.data.l[0] == net_wm_ping_atom) { - fprintf(stderr, "Responding to X11 ping\n"); - xev.xclient.window = DefaultRootWindow(disp); - XSendEvent(disp, DefaultRootWindow(disp), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); - } else if(wm_delete_window_atom && xev.xclient.format == 32 && (Atom)xev.xclient.data.l[0] == wm_delete_window_atom) { - current_page = PageType::EXIT; - window.close(); - window_closed = true; - } + if(window.is_open()) { + window_closed = false; + } else { + window_closed = true; + current_page = PageType::EXIT; } } - void Program::base_event_handler(sf::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_keypress, bool handle_searchbar) { - if(event.type == sf::Event::Resized) { + void Program::base_event_handler(mgl::Event &event, PageType previous_page, Body *body, SearchBar *search_bar, bool handle_keypress, bool handle_searchbar) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); - } else if(handle_keypress && event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Escape) { + } else if(handle_keypress && event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Escape) { current_page = previous_page; } } else if(handle_searchbar) { assert(search_bar); - search_bar->on_event(event); + search_bar->on_event(window, event); } } - void Program::event_idle_handler(const sf::Event &event) { - if(event.type == sf::Event::KeyPressed || event.type == sf::Event::TextEntered) + void Program::event_idle_handler(const mgl::Event &event) { + if(event.type == mgl::Event::KeyPressed || event.type == mgl::Event::TextEntered) idle_active_handler(); } void Program::idle_active_handler() { if(idle) - window.setFramerateLimit(monitor_hz); + window.set_framerate_limit(monitor_hz); idle = false; idle_timer.restart(); } @@ -1351,8 +1331,8 @@ namespace QuickMedia { if(idle) return; - if(idle_timer.getElapsedTime().asSeconds() > IDLE_TIMEOUT_SEC) { - window.setFramerateLimit(FPS_IDLE); + if(idle_timer.get_elapsed_time_seconds() > IDLE_TIMEOUT_SEC) { + window.set_framerate_limit(FPS_IDLE); idle = true; } } @@ -1391,7 +1371,7 @@ namespace QuickMedia { body_item->thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/mqdefault.jpg"; body_item->set_description("Watched " + seconds_to_relative_time_str(time_now - timestamp.asInt64())); body_item->set_description_color(get_theme().faded_text_color); - body_item->thumbnail_size = sf::Vector2i(192, 108); + body_item->thumbnail_size = mgl::vec2i(192, 108); history_items.push_back(std::move(body_item)); } @@ -1428,6 +1408,10 @@ namespace QuickMedia { return json_result; } + void Program::set_clipboard(const std::string &str) { + window.set_clipboard(str); + } + void Program::manga_get_watch_history(const char *plugin_name, BodyItems &history_items) { // TOOD: Make generic, instead of checking for plugin Path content_storage_dir = get_storage_dir().join(plugin_name); @@ -1492,7 +1476,7 @@ namespace QuickMedia { fill_history_items_from_json(load_video_history_json(), history_items); } - static void get_body_dimensions(const sf::Vector2f &window_size, SearchBar *search_bar, sf::Vector2f &body_pos, sf::Vector2f &body_size, bool has_tabs = false) { + static void get_body_dimensions(const mgl::vec2i &window_size, SearchBar *search_bar, mgl::vec2f &body_pos, mgl::vec2f &body_size, bool has_tabs = false) { const float body_width = window_size.x; float tab_h = Tabs::get_shade_height(); @@ -1503,12 +1487,12 @@ namespace QuickMedia { tab_h = 0.0f; float search_bottom = search_bar ? search_bar->getBottomWithoutShadow() : 0.0f; - body_pos = sf::Vector2f(0.0f, search_bottom + tab_h); - body_size = sf::Vector2f(body_width, window_size.y - search_bottom - tab_h); + body_pos = mgl::vec2f(0.0f, search_bottom + tab_h); + body_size = mgl::vec2f(body_width, window_size.y - search_bottom - tab_h); } std::unique_ptr<Body> Program::create_body(bool plain_text_list, bool prefer_card_view) { - if(rounded_rectangle_mask_shader.getNativeHandle() == 0) + if(!rounded_rectangle_mask_shader.is_valid()) plain_text_list = true; auto body = std::make_unique<Body>(plain_text_list ? BODY_THEME_MINIMAL : BODY_THEME_MODERN_SPACIOUS, loading_icon, &rounded_rectangle_shader, &rounded_rectangle_mask_shader); body->card_view = prefer_card_view; @@ -1518,7 +1502,7 @@ namespace QuickMedia { std::unique_ptr<SearchBar> Program::create_search_bar(const std::string &placeholder, int search_delay) { auto search_bar = std::make_unique<SearchBar>(&plugin_logo, &rounded_rectangle_shader, placeholder); - search_bar->text_autosearch_delay = search_delay; + search_bar->text_autosearch_delay_ms = search_delay; return search_bar; } @@ -1556,7 +1540,7 @@ namespace QuickMedia { } bool Program::is_window_focused() { - return window.hasFocus(); + return window.has_focus(); } RoomData* Program::get_current_chat_room() { @@ -1686,38 +1670,38 @@ namespace QuickMedia { return true; } - void Program::page_loop_render(sf::RenderWindow &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs) { - if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->draw(window, window_size, true); + void Program::page_loop_render(mgl::Window &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs) { + if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->draw(window, window_size.to_vec2f(), true); float shade_extra_height = 0.0f; if(!tabs[selected_tab].search_bar) { shade_extra_height = std::floor(10.0f * get_config().scale); - sf::RectangleShape shade_top(sf::Vector2f(window_size.x, shade_extra_height)); - shade_top.setFillColor(get_theme().shade_color); + mgl::Rectangle shade_top(mgl::vec2f(window_size.x, shade_extra_height)); + shade_top.set_color(get_theme().shade_color); window.draw(shade_top); } float tab_vertical_offset = tabs[selected_tab].search_bar ? tabs[selected_tab].search_bar->getBottomWithoutShadow() : 0.0f; - ui_tabs.draw(window, sf::Vector2f(0.0f, tab_vertical_offset + shade_extra_height), window_size.x); + ui_tabs.draw(window, mgl::vec2f(0.0f, tab_vertical_offset + shade_extra_height), window_size.x); tabs[selected_tab].body->draw(window, body_pos, body_size, *json_chapters); if(tab_associated_data.fetching_next_page_running) - window.draw(gradient_points, 4, sf::Quads); // TODO: sf::Quads doesn't work with egl + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); // TODO: mgl::PrimitiveType::Quads doesn't work with egl - if(!tab_associated_data.search_result_text.getString().isEmpty() && !tabs[selected_tab].page->search_is_suggestion()) { - auto search_result_text_bounds = tab_associated_data.search_result_text.getLocalBounds(); - tab_associated_data.search_result_text.setPosition( - std::floor(body_pos.x + body_size.x * 0.5f - search_result_text_bounds.width * 0.5f), - std::floor(body_pos.y + body_size.y * 0.5f - search_result_text_bounds.height * 0.5f)); + if(!tab_associated_data.search_result_text.get_string().empty() && !tabs[selected_tab].page->search_is_suggestion()) { + auto search_result_text_bounds = tab_associated_data.search_result_text.get_bounds(); + tab_associated_data.search_result_text.set_position(mgl::vec2f( + std::floor(body_pos.x + body_size.x * 0.5f - search_result_text_bounds.size.x * 0.5f), + std::floor(body_pos.y + body_size.y * 0.5f - search_result_text_bounds.size.y * 0.5f))); window.draw(tab_associated_data.search_result_text); } if(!tabs[selected_tab].page->is_ready()) { - sf::Text loading_text("Loading...", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(30 * get_config().scale * get_config().font_scale)); - auto text_bounds = loading_text.getLocalBounds(); - loading_text.setPosition( - std::floor(body_pos.x + body_size.x * 0.5f - text_bounds.width * 0.5f), - std::floor(body_pos.y + body_size.y * 0.5f - text_bounds.height * 0.5f)); + mgl::Text loading_text("Loading...", *FontLoader::get_font(FontLoader::FontType::LATIN, 30 * get_config().scale * get_config().font_scale)); + auto text_bounds = loading_text.get_bounds(); + loading_text.set_position(mgl::vec2f( + std::floor(body_pos.x + body_size.x * 0.5f - text_bounds.size.x * 0.5f), + std::floor(body_pos.y + body_size.y * 0.5f - text_bounds.size.y * 0.5f))); window.draw(loading_text); } @@ -1726,8 +1710,8 @@ namespace QuickMedia { if(matrix && !matrix->is_initial_sync_finished()) { // if(is_login_sync) { - load_sprite.setPosition(body_pos.x + body_size.x * 0.5f, body_pos.y + body_size.y * 0.5f); - load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); + load_sprite.set_position(mgl::vec2f(body_pos.x + body_size.x * 0.5f, body_pos.y + body_size.y * 0.5f)); + load_sprite.set_rotation(load_sprite_timer.get_elapsed_time_seconds() * 400.0); window.draw(load_sprite); // } std::string err_msg; @@ -1796,28 +1780,28 @@ namespace QuickMedia { std::vector<TabAssociatedData> tab_associated_data; for(size_t i = 0; i < tabs.size(); ++i) { TabAssociatedData data; - data.search_result_text = sf::Text("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(30 * get_config().scale * get_config().font_scale)); + data.search_result_text = mgl::Text("", *FontLoader::get_font(FontLoader::FontType::LATIN, 30 * get_config().scale * get_config().font_scale)); tab_associated_data.push_back(std::move(data)); } double gradient_inc = 0.0; const float gradient_height = 5.0f; - auto window_size_u = window.getSize(); + auto window_size_u = window.get_size(); window_size.x = window_size_u.x; window_size.y = window_size_u.y; std::function<void(const std::string&)> submit_handler = [this, &submit_handler, &after_submit_handler, &tabs, &tab_associated_data, &ui_tabs, &loop_running, &redraw](const std::string &search_text) { - sf::Event event; - while(window.pollEvent(event)) { common_event_handler(event); } + mgl::Event event; + while(window.poll_event(event)) { common_event_handler(event); } const int selected_tab = ui_tabs.get_selected(); auto selected_item = tabs[selected_tab].body->get_selected_shared(); if(!selected_item && search_text.empty()) return; - if(tabs[selected_tab].page->allow_submit_no_selection() && (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))) - selected_item = nullptr; + if(tabs[selected_tab].page->allow_submit_no_selection() && (window.is_key_pressed(mgl::Keyboard::LControl) || window.is_key_pressed(mgl::Keyboard::RControl))) + selected_item = nullptr; if(!selected_item && !tabs[selected_tab].page->allow_submit_no_selection()) return; @@ -1925,7 +1909,7 @@ namespace QuickMedia { tabs[selected_tab].search_bar->clear(); chapters_body->filter_search_fuzzy(""); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter MangaImagesPage *manga_images_page = static_cast<MangaImagesPage*>(new_tabs[0].page.get()); - window.setKeyRepeatEnabled(false); + window.set_key_repeat_enabled(false); downloading_chapter_url.clear(); Path manga_progress_dir = get_storage_dir().join(manga_images_page->get_service_name()); @@ -1933,7 +1917,7 @@ namespace QuickMedia { show_notification("QuickMedia", "Failed to create directory: " + manga_progress_dir.data, Urgency::CRITICAL); current_page = pop_page_stack(); } else { - while(window.isOpen() && (current_page == PageType::IMAGES || current_page == PageType::IMAGES_CONTINUOUS)) { + while(window.is_open() && (current_page == PageType::IMAGES || current_page == PageType::IMAGES_CONTINUOUS)) { if(current_page == PageType::IMAGES) { while(current_page == PageType::IMAGES) { int page_navigation = image_page(manga_images_page, chapters_body); @@ -1963,7 +1947,7 @@ namespace QuickMedia { image_download_cancel = false; images_to_upscale_queue.clear(); num_manga_pages = 0; - window.setKeyRepeatEnabled(true); + window.set_key_repeat_enabled(true); malloc_trim(0); } else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::IMAGE_BOARD_THREAD) { page_stack.push(current_page); @@ -1986,7 +1970,7 @@ namespace QuickMedia { current_chat_room = matrix->get_room_by_id(tmp_matrix_chat_page->room_id); rooms_page->body->show_drop_shadow = false; - while(window.isOpen() && current_chat_room) { + while(window.is_open() && current_chat_room) { auto matrix_chat_page = std::make_unique<MatrixChatPage>(this, current_chat_room->id, rooms_page, jump_to_event_id); bool move_room = chat_page(matrix_chat_page.get(), current_chat_room); matrix_chat_page->messages_tab_visible = false; @@ -2037,7 +2021,7 @@ namespace QuickMedia { int next_page = tab_associated_data[selected_tab].fetched_page + 1; Page *page = tabs[selected_tab].page.get(); std::string update_search_text = tab_associated_data[selected_tab].update_search_text; - tab_associated_data[selected_tab].next_page_future = AsyncTask<BodyItems>([update_search_text, next_page, page]() { + tab_associated_data[selected_tab].next_page_future = AsyncTask<BodyItems>([update_search_text{std::move(update_search_text)}, next_page, page]() { BodyItems result_items; if(page->get_page(update_search_text, next_page, result_items) != PluginResult::OK) fprintf(stderr, "Failed to get next page (page %d)\n", next_page); @@ -2082,12 +2066,12 @@ namespace QuickMedia { } } - sf::Event event; - sf::Clock frame_timer; + mgl::Event event; + mgl::Clock frame_timer; - while (window.isOpen() && loop_running) { - sf::Int32 frame_time_ms = frame_timer.restart().asMilliseconds(); - while (window.pollEvent(event)) { + while (window.is_open() && loop_running) { + int32_t frame_time_ms = frame_timer.restart() * 1000.0; + while (window.poll_event(event)) { common_event_handler(event); const int selected_tab = ui_tabs.get_selected(); @@ -2096,31 +2080,29 @@ namespace QuickMedia { else event_idle_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); idle_active_handler(); } if(tabs[selected_tab].search_bar) { - tabs[selected_tab].search_bar->on_event(event); + tabs[selected_tab].search_bar->on_event(window, event); } ui_tabs.on_event(event); - if(event.type == sf::Event::Resized || event.type == sf::Event::GainedFocus) + if(event.type == mgl::Event::Resized || event.type == mgl::Event::GainedFocus) redraw = true; - else if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Escape && go_to_previous_on_escape) { + else if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Escape && go_to_previous_on_escape) { return false; - } else if(event.key.code == sf::Keyboard::Enter) { + } else if(event.key.code == mgl::Keyboard::Enter) { if(!tabs[selected_tab].search_bar) { BodyItem *selected_item = tabs[selected_tab].body->get_selected(); submit_handler(selected_item ? selected_item->get_title() : ""); } - } else if(event.key.code == sf::Keyboard::T && event.key.control) { + } else if(event.key.code == mgl::Keyboard::T && event.key.control) { BodyItem *selected_item = tabs[selected_tab].body->get_selected(); if(selected_item && tabs[selected_tab].page->is_trackable()) { TrackablePage *trackable_page = dynamic_cast<TrackablePage*>(tabs[selected_tab].page.get()); @@ -2128,7 +2110,7 @@ namespace QuickMedia { return trackable_page->track(selected_item->get_title()) == TrackResult::OK; }); } - } else if(event.key.code == sf::Keyboard::B && event.key.control) { + } else if(event.key.code == mgl::Keyboard::B && event.key.control) { auto bookmark_item = tabs[selected_tab].page->get_bookmark_body_item(); if(!bookmark_item) bookmark_item = tabs[selected_tab].body->get_selected_shared(); @@ -2143,15 +2125,15 @@ namespace QuickMedia { } } } - } else if(event.key.code == sf::Keyboard::C && event.key.control) { + } else if(event.key.code == mgl::Keyboard::C && event.key.control) { BodyItem *selected_item = tabs[selected_tab].body->get_selected(); if(selected_item) tabs[selected_tab].page->copy_to_clipboard(selected_item); - } else if(event.key.code == sf::Keyboard::I && event.key.control) { + } else if(event.key.code == mgl::Keyboard::I && event.key.control) { BodyItem *selected_item = tabs[selected_tab].body->get_selected(); if(show_info_page(selected_item, false)) redraw = true; - } else if(event.key.code == sf::Keyboard::Tab && !event.key.control) { + } else if(event.key.code == mgl::Keyboard::Tab && !event.key.control) { set_search_bar_to_body_item_text(tabs[selected_tab].body->get_selected(), tabs[selected_tab].search_bar.get()); } } @@ -2159,14 +2141,14 @@ namespace QuickMedia { update_idle_state(); handle_x11_events(); - if(!loop_running || !window.isOpen()) + if(!loop_running || !window.is_open()) break; const int selected_tab = ui_tabs.get_selected(); if(redraw) { redraw = false; - if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->onWindowResize(window_size); + if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->onWindowResize(window_size.to_vec2f()); // TODO: Dont show tabs if there is only one tab get_body_dimensions(window_size, tabs[selected_tab].search_bar.get(), body_pos, body_size, true); @@ -2200,7 +2182,7 @@ namespace QuickMedia { if(tab_associated_data[selected_tab].fetching_next_page_running) { double progress = 0.5 + std::sin(std::fmod(gradient_inc, 360.0) * 0.017453292519943295 - 1.5707963267948966*0.5) * 0.5; gradient_inc += (frame_time_ms * 0.5); - sf::Color bottom_color = interpolate_colors(get_theme().background_color, get_theme().loading_bar_color, progress); + mgl::Color bottom_color = interpolate_colors(get_theme().background_color, get_theme().loading_bar_color, progress); if(tabs[selected_tab].body->attach_side == AttachSide::TOP) { gradient_points[0].color = get_theme().background_color; @@ -2231,7 +2213,7 @@ namespace QuickMedia { if(tabs[selected_tab].page->is_ready() && tabs[selected_tab].page->is_lazy_fetch_page() && tab_associated_data[selected_tab].fetch_status == FetchStatus::NONE && !tab_associated_data[selected_tab].lazy_fetch_finished) { tab_associated_data[selected_tab].fetch_status = FetchStatus::LOADING; tab_associated_data[selected_tab].fetch_type = FetchType::LAZY; - tab_associated_data[selected_tab].search_result_text.setString("Loading..."); + tab_associated_data[selected_tab].search_result_text.set_string("Loading..."); LazyFetchPage *lazy_fetch_page = static_cast<LazyFetchPage*>(tabs[selected_tab].page.get()); tab_associated_data[selected_tab].fetch_future = AsyncTask<FetchResult>([lazy_fetch_page]() { FetchResult fetch_result; @@ -2287,10 +2269,10 @@ namespace QuickMedia { associated_data.search_text_updated = false; associated_data.fetch_status = FetchStatus::LOADING; associated_data.fetch_type = FetchType::SEARCH; - associated_data.search_result_text.setString("Searching..."); + associated_data.search_result_text.set_string("Searching..."); associated_data.search_suggestion_submitted = false; Page *page = tabs[i].page.get(); - associated_data.fetch_future = AsyncTask<FetchResult>([update_search_text, page]() { + associated_data.fetch_future = AsyncTask<FetchResult>([update_search_text{std::move(update_search_text)}, page]() { FetchResult fetch_result; fetch_result.result = search_result_to_plugin_result(page->search(update_search_text, fetch_result.body_items)); return fetch_result; @@ -2310,11 +2292,11 @@ namespace QuickMedia { associated_data.fetched_page = 0; associated_data.fetching_next_page_failed = false; if(fetch_result.result != PluginResult::OK) - associated_data.search_result_text.setString("Search failed!"); + associated_data.search_result_text.set_string("Search failed!"); else if(tabs[i].body->get_num_items() == 0) - associated_data.search_result_text.setString("No results found"); + associated_data.search_result_text.set_string("No results found"); else - associated_data.search_result_text.setString(""); + associated_data.search_result_text.set_string(""); idle_active_handler(); } else { associated_data.fetch_future.get(); @@ -2336,11 +2318,11 @@ namespace QuickMedia { } LazyFetchPage *lazy_fetch_page = static_cast<LazyFetchPage*>(tabs[i].page.get()); if(fetch_result.result != PluginResult::OK) - associated_data.search_result_text.setString("Failed to fetch page!"); + associated_data.search_result_text.set_string("Failed to fetch page!"); else if(tabs[i].body->get_num_items() == 0 && !lazy_fetch_page->lazy_fetch_is_loader()) - associated_data.search_result_text.setString("No results found"); + associated_data.search_result_text.set_string("No results found"); else - associated_data.search_result_text.setString(""); + associated_data.search_result_text.set_string(""); associated_data.fetch_status = FetchStatus::NONE; idle_active_handler(); } @@ -2508,18 +2490,16 @@ namespace QuickMedia { AsyncTask<bool> task = callback; TaskResult task_result = TaskResult::TRUE; - window_size.x = window.getSize().x; - window_size.y = window.getSize().y; - sf::Event event; - while(window.isOpen()) { - while(window.pollEvent(event)) { + window_size.x = window.get_size().x; + window_size.y = window.get_size().y; + mgl::Event event; + while(window.is_open()) { + while(window.poll_event(event)) { common_event_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Escape) { task.cancel(); task_result = TaskResult::CANCEL; goto task_end; @@ -2539,8 +2519,8 @@ namespace QuickMedia { } window.clear(get_theme().background_color); - load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); - load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); + load_sprite.set_position(mgl::vec2f(window_size.x * 0.5f, window_size.y * 0.5f)); + load_sprite.set_rotation(load_sprite_timer.get_elapsed_time_seconds() * 400.0); window.draw(load_sprite); window.display(); } @@ -2573,7 +2553,7 @@ namespace QuickMedia { || is_soundcloud(url); } - void Program::video_page_download_video(const std::string &url, sf::WindowHandle video_player_window) { + void Program::video_page_download_video(const std::string &url, mgl::WindowHandle video_player_window) { bool separate_audio_option = url_should_download_with_youtube_dl(url); std::string video_id; separate_audio_option |= youtube_url_extract_id(url, video_id); @@ -2716,8 +2696,8 @@ namespace QuickMedia { move_in_parent = true; } - sf::WindowHandle video_player_window = None; - auto on_window_create = [this, &video_player_window, &video_loaded, &video_page](sf::WindowHandle _video_player_window) mutable { + mgl::WindowHandle video_player_window = None; + auto on_window_create = [this, &video_player_window, &video_loaded, &video_page](mgl::WindowHandle _video_player_window) mutable { video_player_window = _video_player_window; XSelectInput(disp, video_player_window, KeyPressMask | PointerMotionMask); XSync(disp, False); @@ -2908,8 +2888,7 @@ namespace QuickMedia { if(num_proxied_media > 0) { youtube_downloader_task = AsyncTask<void>([&youtube_video_media_proxy, &youtube_audio_media_proxy]() { - sf::Clock timer; - const double sleep_time_millisec = 1; + mgl::Clock timer; // TODO: Poll instead of sleep while(!program_is_dead_in_current_thread()) { if(youtube_video_media_proxy) @@ -2918,16 +2897,16 @@ namespace QuickMedia { if(youtube_audio_media_proxy) youtube_audio_media_proxy->update(); - const int sleep_time = sleep_time_millisec - timer.restart().asMilliseconds(); - if(sleep_time > 0) - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); + const double sleep_left_sec = timer.restart(); + if(sleep_left_sec > 0.000001) + std::this_thread::sleep_for(std::chrono::milliseconds((long)(sleep_left_sec * 1000.0))); } }); } } video_player = std::make_unique<VideoPlayer>(is_audio_only, get_config().use_system_mpv_config, is_matrix && !is_youtube, video_event_callback, on_window_create, resources_root, video_max_height, plugin_name); - VideoPlayer::Error err = video_player->load_video(v.c_str(), a.c_str(), window.getSystemHandle(), use_youtube_dl, video_title, start_time, media_chapters); + VideoPlayer::Error err = video_player->load_video(v.c_str(), a.c_str(), window.get_system_handle(), use_youtube_dl, video_title, start_time, media_chapters); if(err != VideoPlayer::Error::OK) { std::string err_msg = "Failed to play url: "; err_msg += video_page->get_url(); @@ -2999,11 +2978,11 @@ namespace QuickMedia { load_video_error_check(); - sf::Event event; + mgl::Event event; XEvent xev; bool cursor_visible = true; - sf::Clock cursor_hide_timer; + mgl::Clock cursor_hide_timer; auto save_video_url_to_clipboard = [this, video_page]() { std::string url = video_page->get_download_url(video_get_max_height()); @@ -3016,33 +2995,31 @@ namespace QuickMedia { if((int)time_in_file > 0) clipboard += "&t=" + std::to_string((int)time_in_file); - sf::Clipboard::setString(sf::String::fromUtf8(clipboard.begin(), clipboard.end())); + set_clipboard(clipboard); } else { - sf::Clipboard::setString(sf::String::fromUtf8(url.begin(), url.end())); + set_clipboard(url); } }; - while (current_page == PageType::VIDEO_CONTENT && window.isOpen() && !go_to_previous_page) { - while (window.pollEvent(event)) { + while (current_page == PageType::VIDEO_CONTENT && window.is_open() && !go_to_previous_page) { + while (window.poll_event(event)) { common_event_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); - } else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Escape || event.key.code == sf::Keyboard::Q || event.key.code == sf::Keyboard::Backspace)) { + } else if(event.type == mgl::Event::KeyPressed && (event.key.code == mgl::Keyboard::Escape || event.key.code == mgl::Keyboard::Q || event.key.code == mgl::Keyboard::Backspace)) { // To be able to close the video player while the video is loading - if(window_is_fullscreen(disp, window.getSystemHandle())) { - window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET); + if(window_is_fullscreen(disp, window.get_system_handle())) { + window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::UNSET); } else { current_page = previous_page; go_to_previous_page = true; } - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F && event.key.control) { - window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE); - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::C && event.key.control) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::F && event.key.control) { + window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::TOGGLE); + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::C && event.key.control) { save_video_url_to_clipboard(); - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::F5) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::F5) { load_video_error_check(); } } @@ -3057,15 +3034,15 @@ namespace QuickMedia { if(pressed_keysym == XK_q && pressing_ctrl) { window.close(); } else if(pressed_keysym == XK_Escape || pressed_keysym == XK_q || pressed_keysym == XK_BackSpace) { - if(window_is_fullscreen(disp, window.getSystemHandle())) { - window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET); + if(window_is_fullscreen(disp, window.get_system_handle())) { + window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::UNSET); } else { current_page = previous_page; go_to_previous_page = true; break; } } else if(pressed_keysym == XK_f && pressing_ctrl) { - window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::TOGGLE); + window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::TOGGLE); } else if(pressed_keysym == XK_s && pressing_ctrl) { video_page_download_video(video_page->get_download_url(video_get_max_height()), video_player_window); } else if(pressed_keysym == XK_F5) { @@ -3077,6 +3054,7 @@ namespace QuickMedia { if(video_tasks.valid()) { XUnmapWindow(disp, video_player_window); XSync(disp, False); + XFlush(disp); TaskResult task_result = run_task_with_loading_screen([video_page, &related_videos]() { related_videos = video_page->get_related_media(video_page->get_url()); @@ -3092,7 +3070,7 @@ namespace QuickMedia { if(!cancelled) { if(!cursor_visible) - window.setMouseCursorVisible(true); + window.set_cursor_visible(true); cursor_visible = true; XUnmapWindow(disp, video_player_window); @@ -3108,6 +3086,10 @@ namespace QuickMedia { XSync(disp, False); show_notification("QuickMedia", "Failed to get related pages", Urgency::CRITICAL); } else if(related_pages_result == TaskResult::TRUE) { + XMapWindow(disp, video_player_window); + XSync(disp, False); + XFlush(disp); + bool page_changed = false; double resume_start_time = 0.0; page_loop(related_pages, video_page->get_related_pages_first_tab(), [&](const std::vector<Tab> &new_tabs) { @@ -3128,7 +3110,7 @@ namespace QuickMedia { } }); - if(!window.isOpen() || current_page == PageType::EXIT) { + if(!window.is_open() || current_page == PageType::EXIT) { video_player.reset(); return; } @@ -3154,7 +3136,7 @@ namespace QuickMedia { while(XCheckTypedWindowEvent(disp, video_player_window, MotionNotify, &xev)); cursor_hide_timer.restart(); if(!cursor_visible) - window.setMouseCursorVisible(true); + window.set_cursor_visible(true); cursor_visible = true; } @@ -3270,14 +3252,14 @@ namespace QuickMedia { if(!video_loaded) { window.clear(get_theme().background_color); - load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); - load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); + load_sprite.set_position(mgl::vec2f(window_size.x * 0.5f, window_size.y * 0.5f)); + load_sprite.set_rotation(load_sprite_timer.get_elapsed_time_seconds() * 400.0); window.draw(load_sprite); window.display(); if(!cursor_visible) { cursor_visible = true; - window.setMouseCursorVisible(true); + window.set_cursor_visible(true); } cursor_hide_timer.restart(); continue; @@ -3289,20 +3271,20 @@ namespace QuickMedia { continue; } - const int UI_HIDE_TIMEOUT = 2500; - if(cursor_hide_timer.getElapsedTime().asMilliseconds() > UI_HIDE_TIMEOUT) { + const double UI_HIDE_TIMEOUT_SEC = 2.5; + if(cursor_hide_timer.get_elapsed_time_seconds() > UI_HIDE_TIMEOUT_SEC) { cursor_visible = false; - window.setMouseCursorVisible(false); + window.set_cursor_visible(false); } } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } video_player.reset(); - window.setMouseCursorVisible(true); - window_set_fullscreen(disp, window.getSystemHandle(), WindowFullscreenState::UNSET); + window.set_cursor_visible(true); + window_set_fullscreen(disp, window.get_system_handle(), WindowFullscreenState::UNSET); - auto window_size_u = window.getSize(); + auto window_size_u = window.get_size(); window_size.x = window_size_u.x; window_size.y = window_size_u.y; } @@ -3344,7 +3326,7 @@ namespace QuickMedia { // TODO: Optimize this somehow. One image alone uses more than 20mb ram! Total ram usage for viewing one image // becomes 40mb (private memory, almost 100mb in total!) Unacceptable! - Program::LoadImageResult Program::load_image_by_index(int image_index, sf::Texture &image_texture, sf::String &error_message) { + Program::LoadImageResult Program::load_image_by_index(int image_index, mgl::Texture &image_texture, std::string &error_message) { Path image_path = content_cache_dir; image_path.join(std::to_string(image_index + 1)); @@ -3357,16 +3339,8 @@ namespace QuickMedia { } if(get_file_type(image_path) == FileType::REGULAR && upscaled_ok) { - sf::Image image; - if(load_image_from_file(image, image_path.data)) { - if(image_texture.loadFromImage(image)) { - image_texture.setSmooth(true); - //image_texture.generateMipmap(); - return LoadImageResult::OK; - } else { - error_message = std::string("Failed to load image for page ") + std::to_string(image_index + 1); - return LoadImageResult::FAILED; - } + if(image_texture.load_from_file(image_path.data.c_str())) { + return LoadImageResult::OK; } else { show_notification("QuickMedia", "Failed to load image for page " + std::to_string(image_index + 1) + ". Image filepath: " + image_path.data, Urgency::CRITICAL); error_message = std::string("Failed to load image for page ") + std::to_string(image_index + 1); @@ -3501,18 +3475,16 @@ namespace QuickMedia { }); }, std::move(num_manga_pages_promise)); - sf::Event event; + mgl::Event event; PageType current_manga_page = current_page; - while (current_page == current_manga_page && window.isOpen()) { - while(window.pollEvent(event)) { + while (current_page == current_manga_page && window.is_open()) { + while(window.poll_event(event)) { common_event_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Escape) { current_page = pop_page_stack(); } } @@ -3524,8 +3496,8 @@ namespace QuickMedia { } window.clear(get_theme().background_color); - load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); - load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); + load_sprite.set_position(mgl::vec2f(window_size.x * 0.5f, window_size.y * 0.5f)); + load_sprite.set_rotation(load_sprite_timer.get_elapsed_time_seconds() * 400.0); window.draw(load_sprite); AsyncImageLoader::get_instance().update(); window.display(); @@ -3572,13 +3544,13 @@ namespace QuickMedia { return 0; } - sf::Texture image_texture; - sf::Sprite image; - sf::Text error_message("", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(30 * get_config().scale * get_config().font_scale)); - error_message.setFillColor(get_theme().text_color); + mgl::Texture image_texture; + mgl::Sprite image; + mgl::Text error_message("", *FontLoader::get_font(FontLoader::FontType::LATIN, 30 * get_config().scale * get_config().font_scale)); + error_message.set_color(get_theme().text_color); bool download_in_progress = false; - sf::Event event; + mgl::Event event; download_chapter_images_if_needed(images_page); if(num_manga_pages == 0) { @@ -3586,7 +3558,7 @@ namespace QuickMedia { return 0; } - if(current_page != PageType::IMAGES || !window.isOpen()) + if(current_page != PageType::IMAGES || !window.is_open()) return 0; // TODO: Dont do this every time we change page? @@ -3596,57 +3568,56 @@ namespace QuickMedia { save_manga_progress(images_page, json_chapters, json_chapter, latest_read); if(image_index < num_manga_pages) { - sf::String error_msg; + std::string error_msg; LoadImageResult load_image_result = load_image_by_index(image_index, image_texture, error_msg); if(load_image_result == LoadImageResult::OK) - image.setTexture(image_texture, true); + image.set_texture(&image_texture); else if(load_image_result == LoadImageResult::DOWNLOAD_IN_PROGRESS) download_in_progress = true; - error_message.setString(error_msg); + error_message.set_string(std::move(error_msg)); } else if(image_index == num_manga_pages) { - error_message.setString("End of " + images_page->get_chapter_name()); + error_message.set_string("End of " + images_page->get_chapter_name()); } - bool error = !error_message.getString().isEmpty(); + bool error = !error_message.get_string().empty(); bool redraw = true; - sf::Text chapter_text(images_page->manga_name + " | " + images_page->get_chapter_name() + " | Page " + std::to_string(image_index + 1) + "/" + std::to_string(num_manga_pages), *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14 * get_config().scale * get_config().font_scale)); + const int chapter_text_character_size = 14 * get_config().scale * get_config().font_scale; + mgl::Text chapter_text(images_page->manga_name + " | " + images_page->get_chapter_name() + " | Page " + std::to_string(image_index + 1) + "/" + std::to_string(num_manga_pages), *FontLoader::get_font(FontLoader::FontType::LATIN, chapter_text_character_size)); if(image_index == num_manga_pages) - chapter_text.setString(images_page->manga_name + " | " + images_page->get_chapter_name() + " | End"); - chapter_text.setFillColor(sf::Color::White); - sf::RectangleShape chapter_text_background; - chapter_text_background.setFillColor(sf::Color(0, 0, 0, 150)); + chapter_text.set_string(images_page->manga_name + " | " + images_page->get_chapter_name() + " | End"); + chapter_text.set_color(mgl::Color(255, 255, 255, 255)); + mgl::Rectangle chapter_text_background; + chapter_text_background.set_color(mgl::Color(0, 0, 0, 150)); - sf::Vector2u texture_size; - sf::Vector2f texture_size_f; + mgl::vec2i texture_size; + mgl::vec2f texture_size_f; if(!error) { - texture_size = image.getTexture()->getSize(); - texture_size_f = sf::Vector2f(texture_size.x, texture_size.y); + texture_size = image.get_texture()->get_size(); + texture_size_f = mgl::vec2f(texture_size.x, texture_size.y); } - sf::Clock check_downloaded_timer; - const sf::Int32 check_downloaded_timeout_ms = 500; + mgl::Clock check_downloaded_timer; + const double check_downloaded_timeout_sec = 0.5; malloc_trim(0); - sf::Clock force_redraw_timer; - window.setFramerateLimit(20); + mgl::Clock force_redraw_timer; + window.set_framerate_limit(20); idle = true; // TODO: Show to user if a certain page is missing (by checking page name (number) and checking if some are skipped) - while (current_page == PageType::IMAGES && window.isOpen()) { - while(window.pollEvent(event)) { + while (current_page == PageType::IMAGES && window.is_open()) { + while(window.poll_event(event)) { common_event_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); redraw = true; - } else if(event.type == sf::Event::GainedFocus) { + } else if(event.type == mgl::Event::GainedFocus) { redraw = true; - } else if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) { + } else if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Up || (event.key.control && event.key.code == mgl::Keyboard::K)) { if(image_index > 0) { --image_index; goto end_of_images_page; @@ -3654,7 +3625,7 @@ namespace QuickMedia { page_navigation = -1; goto end_of_images_page; } - } else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) { + } else if(event.key.code == mgl::Keyboard::Down || (event.key.control && event.key.code == mgl::Keyboard::J)) { if(image_index < num_manga_pages) { ++image_index; goto end_of_images_page; @@ -3662,12 +3633,12 @@ namespace QuickMedia { page_navigation = 1; goto end_of_images_page; } - } else if(event.key.code == sf::Keyboard::Escape) { + } else if(event.key.code == mgl::Keyboard::Escape) { current_page = pop_page_stack(); - } else if(event.key.code == sf::Keyboard::I) { + } else if(event.key.code == mgl::Keyboard::I) { current_page = PageType::IMAGES_CONTINUOUS; image_view_mode = ImageViewMode::SCROLL; - } else if(event.key.code == sf::Keyboard::F) { + } else if(event.key.code == mgl::Keyboard::F) { fit_image_to_window = !fit_image_to_window; redraw = true; } @@ -3675,33 +3646,33 @@ namespace QuickMedia { } handle_x11_events(); - if(download_in_progress && check_downloaded_timer.getElapsedTime().asMilliseconds() >= check_downloaded_timeout_ms) { - sf::String error_msg; + if(download_in_progress && check_downloaded_timer.get_elapsed_time_seconds() >= check_downloaded_timeout_sec) { + std::string error_msg; LoadImageResult load_image_result = load_image_by_index(image_index, image_texture, error_msg); if(load_image_result == LoadImageResult::OK) { - image.setTexture(image_texture, true); + image.set_texture(&image_texture); download_in_progress = false; error = false; - texture_size = image.getTexture()->getSize(); - texture_size_f = sf::Vector2f(texture_size.x, texture_size.y); + texture_size = image.get_texture()->get_size(); + texture_size_f = mgl::vec2f(texture_size.x, texture_size.y); } else if(load_image_result == LoadImageResult::FAILED) { download_in_progress = false; error = true; } - error_message.setString(error_msg); + error_message.set_string(std::move(error_msg)); redraw = true; check_downloaded_timer.restart(); } - const float font_height = chapter_text.getCharacterSize() + 8.0f; + const float font_height = chapter_text_character_size + 8.0f; const float bottom_panel_height = font_height + 6.0f; - sf::Vector2f content_size; + mgl::vec2f content_size; content_size.x = window_size.x; content_size.y = window_size.y - bottom_panel_height; // TODO: Track x11 window damage instead - if(force_redraw_timer.getElapsedTime().asSeconds() >= 1.0f) { + if(force_redraw_timer.get_elapsed_time_seconds() >= 1.0f) { force_redraw_timer.restart(); redraw = true; } @@ -3709,20 +3680,20 @@ namespace QuickMedia { if(redraw) { redraw = false; if(error) { - auto bounds = error_message.getLocalBounds(); - error_message.setPosition(vec2f_floor(content_size.x * 0.5f - bounds.width * 0.5f, content_size.y * 0.5f - bounds.height)); + auto bounds = error_message.get_bounds(); + error_message.set_position(vec2f_floor(content_size.x * 0.5f - bounds.size.x * 0.5f, content_size.y * 0.5f - bounds.size.y)); } else { - sf::Vector2f image_scale; + mgl::vec2f image_scale; if(fit_image_to_window) image_scale = get_ratio(texture_size_f, wrap_to_size(texture_size_f, content_size)); else image_scale = get_ratio(texture_size_f, clamp_to_size(texture_size_f, content_size)); - image.setScale(image_scale); + image.set_scale(image_scale); auto image_size = texture_size_f; image_size.x *= image_scale.x; image_size.y *= image_scale.y; - image.setPosition(vec2f_floor(content_size.x * 0.5f - image_size.x * 0.5f, content_size.y * 0.5f - image_size.y * 0.5f)); + image.set_position(vec2f_floor(content_size.x * 0.5f - image_size.x * 0.5f, content_size.y * 0.5f - image_size.y * 0.5f)); } window.clear(get_theme().background_color); @@ -3733,12 +3704,12 @@ namespace QuickMedia { window.draw(image); } - chapter_text_background.setSize(sf::Vector2f(window_size.x, bottom_panel_height)); - chapter_text_background.setPosition(0.0f, std::floor(window_size.y - bottom_panel_height)); + chapter_text_background.set_size(mgl::vec2f(window_size.x, bottom_panel_height)); + chapter_text_background.set_position(mgl::vec2f(0.0f, std::floor(window_size.y - bottom_panel_height))); window.draw(chapter_text_background); - auto text_bounds = chapter_text.getLocalBounds(); - chapter_text.setPosition(vec2f_floor(window_size.x * 0.5f - text_bounds.width * 0.5f, window_size.y - bottom_panel_height * 0.5f - font_height * 0.5f)); + auto text_bounds = chapter_text.get_bounds(); + chapter_text.set_position(vec2f_floor(window_size.x * 0.5f - text_bounds.size.x * 0.5f, window_size.y - bottom_panel_height * 0.5f - font_height * 0.6f)); window.draw(chapter_text); window.display(); @@ -3767,7 +3738,7 @@ namespace QuickMedia { return; } - if(current_page != PageType::IMAGES_CONTINUOUS || !window.isOpen()) + if(current_page != PageType::IMAGES_CONTINUOUS || !window.is_open()) return; Json::Value json_chapters; @@ -3778,7 +3749,7 @@ namespace QuickMedia { idle_active_handler(); - while(current_page == PageType::IMAGES_CONTINUOUS && window.isOpen()) { + while(current_page == PageType::IMAGES_CONTINUOUS && window.is_open()) { handle_x11_events(); window.clear(get_theme().background_color); ImageViewerAction action = image_viewer.draw(); @@ -3809,8 +3780,8 @@ namespace QuickMedia { } } - window_size.x = window.getSize().x; - window_size.y = window.getSize().y; + window_size.x = window.get_size().x; + window_size.y = window.get_size().y; } static bool get_image_board_last_posted_filepath(const char *plugin_name, Path &path) { @@ -3826,19 +3797,19 @@ namespace QuickMedia { float zoom = 1.0f; bool pressed = false; bool moved = false; - sf::Vector2f offset; // relative to center - sf::Vector2i prev_mouse_pos; + mgl::vec2f offset; // relative to center + mgl::vec2i prev_mouse_pos; }; - static sf::Vector2f to_vec2f(sf::Vector2i vec) { - return sf::Vector2f(vec.x, vec.y); + static mgl::vec2f to_vec2f(mgl::vec2i vec) { + return mgl::vec2f(vec.x, vec.y); } - static sf::Vector2f to_vec2f(sf::Vector2u vec) { - return sf::Vector2f(vec.x, vec.y); + static mgl::vec2f to_vec2f(mgl::vec2u vec) { + return mgl::vec2f(vec.x, vec.y); } - static sf::Vector2f floor(sf::Vector2f vec) { + static mgl::vec2f floor(mgl::vec2f vec) { return { std::floor(vec.x), std::floor(vec.y) }; } @@ -3856,10 +3827,10 @@ namespace QuickMedia { thread_body->title_mark_urls = true; NavigationStage navigation_stage = NavigationStage::VIEWING_COMMENTS; - sf::Texture captcha_texture; - sf::Sprite captcha_sprite; - sf::Texture captcha_bg_texture; - sf::Sprite captcha_bg_sprite; + mgl::Texture captcha_texture; + mgl::Sprite captcha_sprite; + mgl::Texture captcha_bg_texture; + mgl::Sprite captcha_bg_sprite; bool has_captcha_bg = false; float captcha_slide = 0.0f; std::string attached_image_url; @@ -3867,26 +3838,23 @@ namespace QuickMedia { ImageControl image_control; - captcha_texture.setSmooth(true); - captcha_bg_texture.setSmooth(true); - const float captcha_slide_padding_x = std::floor(4.0f * get_config().scale); const float captcha_slide_padding_y = std::floor(4.0f * get_config().scale); - sf::Color background_color_darker = get_theme().background_color; + mgl::Color background_color_darker = get_theme().background_color; background_color_darker.r = std::max(0, (int)background_color_darker.r - 20); background_color_darker.g = std::max(0, (int)background_color_darker.g - 20); background_color_darker.b = std::max(0, (int)background_color_darker.b - 20); - RoundedRectangle captcha_slide_bg(sf::Vector2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale), background_color_darker, &rounded_rectangle_shader); - RoundedRectangle captcha_slide_fg(sf::Vector2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale - captcha_slide_padding_y), get_theme().loading_bar_color, &rounded_rectangle_shader); + RoundedRectangle captcha_slide_bg(mgl::vec2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale), background_color_darker, &rounded_rectangle_shader); + RoundedRectangle captcha_slide_fg(mgl::vec2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale - captcha_slide_padding_y), get_theme().loading_bar_color, &rounded_rectangle_shader); - auto attached_image_texture = std::make_unique<sf::Texture>(); - sf::Sprite attached_image_sprite; + auto attached_image_texture = std::make_unique<mgl::Texture>(); + mgl::Sprite attached_image_sprite; std::string captcha_post_id; std::string captcha_solution; std::string comment_to_post; const int captcha_solution_text_height = 18 * get_config().scale * get_config().font_scale; - sf::Text captcha_solution_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD), captcha_solution_text_height); + mgl::Text captcha_solution_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, captcha_solution_text_height)); int solved_captcha_ttl = 0; int64_t last_posted_time = time(nullptr); int64_t seconds_until_post_again = 60; // TODO: Timeout for other imageboards @@ -3983,14 +3951,14 @@ namespace QuickMedia { return false; }; - sf::RectangleShape comment_input_shade; - comment_input_shade.setFillColor(get_theme().shade_color); + mgl::Rectangle comment_input_shade; + comment_input_shade.set_color(get_theme().shade_color); - sf::Sprite logo_sprite(plugin_logo); - logo_sprite.setScale(0.8f * get_config().scale, 0.8f * get_config().scale); - sf::Vector2f logo_size(std::floor(plugin_logo.getSize().x * logo_sprite.getScale().x), std::floor(plugin_logo.getSize().y * logo_sprite.getScale().y)); + mgl::Sprite logo_sprite(&plugin_logo); + logo_sprite.set_scale(mgl::vec2f(0.8f * get_config().scale, 0.8f * get_config().scale)); + mgl::vec2f logo_size(std::floor(plugin_logo.get_size().x * logo_sprite.get_scale().x), std::floor(plugin_logo.get_size().y * logo_sprite.get_scale().y)); - sf::Sprite file_to_upload_sprite; + mgl::Sprite file_to_upload_sprite; bool sprite_applied_texture = false; std::shared_ptr<ThumbnailData> file_to_upload_thumbnail_data; @@ -4002,17 +3970,17 @@ namespace QuickMedia { const float chat_input_padding_x = std::floor(10.0f * get_config().scale); const float chat_input_padding_y = std::floor(10.0f * get_config().scale); - sf::Vector2f body_pos; - sf::Vector2f body_size; - sf::Event event; + mgl::vec2f body_pos; + mgl::vec2f body_size; + mgl::Event event; std::deque<int> comment_navigation_stack; std::deque<int> comment_page_scroll_stack; - sf::Clock frame_timer; - while (current_page == PageType::IMAGE_BOARD_THREAD && window.isOpen()) { - const float frame_elapsed_time_sec = frame_timer.restart().asSeconds(); - while (window.pollEvent(event)) { + mgl::Clock frame_timer; + while (current_page == PageType::IMAGE_BOARD_THREAD && window.is_open()) { + const float frame_elapsed_time_sec = frame_timer.restart(); + while (window.poll_event(event)) { common_event_handler(event); if(navigation_stage == NavigationStage::REPLYING || navigation_stage == NavigationStage::VIEWING_COMMENTS) { if(thread_body->on_event(window, event, navigation_stage == NavigationStage::VIEWING_COMMENTS)) @@ -4021,30 +3989,28 @@ namespace QuickMedia { event_idle_handler(event); if(!frame_skip_text_entry) - comment_input.process_event(event); + comment_input.process_event(window, event); if(navigation_stage == NavigationStage::REPLYING && !frame_skip_text_entry) { - if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Escape) { comment_input.set_editable(false); navigation_stage = NavigationStage::VIEWING_COMMENTS; break; } } - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); idle_active_handler(); } - if(event.type == sf::Event::Resized || event.type == sf::Event::GainedFocus) + if(event.type == mgl::Event::Resized || event.type == mgl::Event::GainedFocus) redraw = true; - else if(navigation_stage == NavigationStage::VIEWING_COMMENTS && event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Escape) { + else if(navigation_stage == NavigationStage::VIEWING_COMMENTS && event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Escape) { current_page = pop_page_stack(); - } else if(event.key.code == sf::Keyboard::P) { + } else if(event.key.code == mgl::Keyboard::P) { BodyItem *selected_item = thread_body->get_selected(); if(selected_item && !selected_item->url.empty()) { if(is_url_video(selected_item->url)) { @@ -4066,7 +4032,7 @@ namespace QuickMedia { BodyItem *selected_item = thread_body->get_selected(); if(selected_item && !selected_item->url.empty()) { attached_image_url = selected_item->url; - sf::Image image; + mgl::Image image; TaskResult task_result = run_task_with_loading_screen([&attached_image_url, &image]{ SHA256 sha256; sha256.add(attached_image_url.data(), attached_image_url.size()); @@ -4077,7 +4043,7 @@ namespace QuickMedia { return false; } - if(!load_image_from_file(image, media_file_path.data)) { + if(!image.load_from_file(media_file_path.data.c_str())) { show_notification("QuickMedia", "Failed to load image: " + attached_image_url, Urgency::CRITICAL); return false; } @@ -4085,20 +4051,21 @@ namespace QuickMedia { }); if(task_result == TaskResult::TRUE) { - attached_image_texture = std::make_unique<sf::Texture>(); - if(attached_image_texture->loadFromImage(image)) { - attached_image_texture->setSmooth(true); - attached_image_sprite.setTexture(*attached_image_texture, true); - attached_image_sprite.setOrigin(0.0f, 0.0f); - attached_image_sprite.setScale(1.0f, 1.0f); + attached_image_texture = std::make_unique<mgl::Texture>(); + if(attached_image_texture->load_from_image(image)) { + attached_image_sprite.set_texture(attached_image_texture.get()); + attached_image_sprite.set_origin(mgl::vec2f(0.0f, 0.0f)); + attached_image_sprite.set_scale(mgl::vec2f(1.0f, 1.0f)); navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; image_control.zoom = 1.0f; - image_control.offset = to_vec2f(attached_image_texture->getSize()) * 0.5f; - image_control.pressed = sf::Mouse::isButtonPressed(sf::Mouse::Left); + image_control.offset = to_vec2f(attached_image_texture->get_size()) * 0.5f; + image_control.pressed = window.is_mouse_button_pressed(mgl::Mouse::Left); + image_control.pressed = false; + image_control.moved = false; if(image_control.pressed) - image_control.prev_mouse_pos = sf::Mouse::getPosition(window); + image_control.prev_mouse_pos = window.get_mouse_position(); } else { show_notification("QuickMedia", "Failed to load image: " + attached_image_url, Urgency::CRITICAL); } @@ -4106,7 +4073,7 @@ namespace QuickMedia { } } } - } else if(event.key.code == sf::Keyboard::U) { + } else if(event.key.code == mgl::Keyboard::U) { std::filesystem::path &fm_dir = file_manager_start_dir; auto file_manager_page = std::make_unique<FileManagerPage>(this, (FileManagerMimeType)(FILE_MANAGER_MIME_TYPE_IMAGE|FILE_MANAGER_MIME_TYPE_VIDEO)); file_manager_page->set_current_directory(fm_dir.string()); @@ -4117,8 +4084,8 @@ namespace QuickMedia { std::vector<Tab> file_manager_tabs; file_manager_tabs.push_back(Tab{std::move(file_manager_body), std::move(file_manager_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); - sf::Event event; - while(window.pollEvent(event)) { common_event_handler(event); } + mgl::Event event; + while(window.poll_event(event)) { common_event_handler(event); } selected_files.clear(); page_loop(file_manager_tabs); @@ -4131,26 +4098,26 @@ namespace QuickMedia { redraw = true; frame_skip_text_entry = true; - } else if(event.key.code == sf::Keyboard::D && event.key.control) { + } else if(event.key.code == mgl::Keyboard::D && event.key.control) { selected_file_for_upload.clear(); - } else if(event.key.code == sf::Keyboard::C && event.key.control) { + } else if(event.key.code == mgl::Keyboard::C && event.key.control) { BodyItem *selected_item = thread_body->get_selected(); if(selected_item) thread_page->copy_to_clipboard(selected_item); - } else if(event.key.code == sf::Keyboard::I && event.key.control) { + } else if(event.key.code == mgl::Keyboard::I && event.key.control) { BodyItem *selected_item = thread_body->get_selected(); if(show_info_page(selected_item, true)) { redraw = true; frame_skip_text_entry = true; } - } else if(event.key.code == sf::Keyboard::S && event.key.control) { + } else if(event.key.code == mgl::Keyboard::S && event.key.control) { BodyItem *selected_item = thread_body->get_selected(); if(selected_item && !selected_item->url.empty()) download_async_gui(selected_item->url, file_manager_start_dir.string(), false); } BodyItem *selected_item = thread_body->get_selected(); - if(event.key.code == sf::Keyboard::Enter && selected_item && (comment_navigation_stack.empty() || thread_body->get_selected_item() != comment_navigation_stack.back()) && (!selected_item->replies_to.empty() || !selected_item->replies.empty())) { + if(event.key.code == mgl::Keyboard::Enter && selected_item && (comment_navigation_stack.empty() || thread_body->get_selected_item() != comment_navigation_stack.back()) && (!selected_item->replies_to.empty() || !selected_item->replies.empty())) { thread_body->for_each_item([](std::shared_ptr<BodyItem> &body_item) { body_item->visible = false; }); @@ -4165,7 +4132,7 @@ namespace QuickMedia { comment_page_scroll_stack.push_back(thread_body->get_page_scroll()); //thread_body->clamp_selection(); thread_body->set_page_scroll(0.0f); - } else if(event.key.code == sf::Keyboard::BackSpace && !comment_navigation_stack.empty()) { + } else if(event.key.code == mgl::Keyboard::Backspace && !comment_navigation_stack.empty()) { size_t previous_selected = comment_navigation_stack.back(); float previous_page_scroll = comment_page_scroll_stack.back(); comment_navigation_stack.pop_back(); @@ -4192,13 +4159,13 @@ namespace QuickMedia { thread_body->clamp_selection(); } thread_body->set_page_scroll(previous_page_scroll); - } else if(event.key.code == sf::Keyboard::R && selected_item) { + } else if(event.key.code == mgl::Keyboard::R && selected_item) { std::string text_to_add = ">>" + selected_item->post_number + "\n"; - comment_input.append_text(std::move(text_to_add)); + comment_input.insert_text_at_caret_position(std::move(text_to_add)); comment_input.move_caret_to_end(); } - if(event.key.code == sf::Keyboard::I && !event.key.control) { + if(event.key.code == mgl::Keyboard::I && !event.key.control) { frame_skip_text_entry = true; navigation_stage = NavigationStage::REPLYING; comment_input.set_editable(true); @@ -4206,75 +4173,74 @@ namespace QuickMedia { } } - if(event.type == sf::Event::KeyPressed && navigation_stage == NavigationStage::SOLVING_POST_CAPTCHA && !frame_skip_text_entry) { - if(event.key.code == sf::Keyboard::Escape) { + if(event.type == mgl::Event::KeyPressed && navigation_stage == NavigationStage::SOLVING_POST_CAPTCHA && !frame_skip_text_entry) { + if(event.key.code == mgl::Keyboard::Escape) { navigation_stage = NavigationStage::VIEWING_COMMENTS; - } else if(event.key.code == sf::Keyboard::Enter) { + } else if(event.key.code == mgl::Keyboard::Enter) { navigation_stage = NavigationStage::POSTING_COMMENT; - auto str8 = captcha_solution_text.getString().toUtf8(); - captcha_solution.assign(str8.begin(), str8.end()); - } else if(event.key.code == sf::Keyboard::BackSpace) { - auto str = captcha_solution_text.getString(); - if(!str.isEmpty()) { - str.erase(str.getSize() - 1); - captcha_solution_text.setString(std::move(str)); + captcha_solution = captcha_solution_text.get_string(); + } else if(event.key.code == mgl::Keyboard::Backspace) { + std::string str = captcha_solution_text.get_string(); + if(!str.empty()) { + str.erase(str.size() - 1); + captcha_solution_text.set_string(std::move(str)); } } else { - const int alpha = (int)event.key.code - (int)sf::Keyboard::A; - const int num = (int)event.key.code - (int)sf::Keyboard::Num0; - const int numpad = (int)event.key.code - (int)sf::Keyboard::Numpad0; - if(alpha >= 0 && alpha <= sf::Keyboard::Z - sf::Keyboard::A) { - captcha_solution_text.setString(captcha_solution_text.getString() + (sf::Uint32)to_upper(alpha + 'a')); - } else if(num >= 0 && num <= sf::Keyboard::Num9 - sf::Keyboard::Num0) { - captcha_solution_text.setString(captcha_solution_text.getString() + (sf::Uint32)(num + '0')); - } else if(numpad >= 0 && numpad <= sf::Keyboard::Numpad9 - sf::Keyboard::Numpad0) { - captcha_solution_text.setString(captcha_solution_text.getString() + (sf::Uint32)(numpad + '0')); + const int alpha = (int)event.key.code - (int)mgl::Keyboard::A; + const int num = (int)event.key.code - (int)mgl::Keyboard::Num0; + const int numpad = (int)event.key.code - (int)mgl::Keyboard::Numpad0; + if(alpha >= 0 && alpha <= mgl::Keyboard::Z - mgl::Keyboard::A) { + captcha_solution_text.set_string(captcha_solution_text.get_string() + (char)to_upper(alpha + 'a')); + } else if(num >= 0 && num <= mgl::Keyboard::Num9 - mgl::Keyboard::Num0) { + captcha_solution_text.set_string(captcha_solution_text.get_string() + (char)(num + '0')); + } else if(numpad >= 0 && numpad <= mgl::Keyboard::Numpad9 - mgl::Keyboard::Numpad0) { + captcha_solution_text.set_string(captcha_solution_text.get_string() + (char)(numpad + '0')); } } } if(navigation_stage == NavigationStage::VIEWING_ATTACHED_IMAGE) { - if(event.type == sf::Event::MouseWheelScrolled) { - image_control.zoom *= (1.0f + event.mouseWheelScroll.delta * 0.1f); + if(event.type == mgl::Event::MouseWheelScrolled) { + image_control.zoom *= (1.0f + event.mouse_wheel_scroll.delta * 0.1f); if(image_control.zoom < 0.01f) image_control.zoom = 0.01f; image_control.moved = true; - } else if(event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) { + } else if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { image_control.pressed = true; - image_control.prev_mouse_pos.x = event.mouseButton.x; - image_control.prev_mouse_pos.y = event.mouseButton.y; - } else if(event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left) { + image_control.prev_mouse_pos.x = event.mouse_button.x; + image_control.prev_mouse_pos.y = event.mouse_button.y; + } else if(event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Left) { image_control.pressed = false; - } else if(event.type == sf::Event::MouseMoved && image_control.pressed) { - const sf::Vector2i mouse_diff = sf::Vector2i(event.mouseMove.x, event.mouseMove.y) - image_control.prev_mouse_pos; - image_control.prev_mouse_pos.x = event.mouseMove.x; - image_control.prev_mouse_pos.y = event.mouseMove.y; + } else if(event.type == mgl::Event::MouseMoved && image_control.pressed) { + const mgl::vec2i mouse_diff = mgl::vec2i(event.mouse_move.x, event.mouse_move.y) - image_control.prev_mouse_pos; + image_control.prev_mouse_pos.x = event.mouse_move.x; + image_control.prev_mouse_pos.y = event.mouse_move.y; image_control.offset -= (to_vec2f(mouse_diff) / image_control.zoom); image_control.moved = true; idle_active_handler(); - } else if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::W) { + } else if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::W) { image_control.zoom = 1.0f; image_control.moved = false; - image_control.offset = to_vec2f(attached_image_texture->getSize()) * 0.5f; - attached_image_sprite.setOrigin(0.0f, 0.0f); - attached_image_sprite.setScale(1.0f, 1.0f); + image_control.offset = to_vec2f(attached_image_texture->get_size()) * 0.5f; + attached_image_sprite.set_origin(mgl::vec2f(0.0f, 0.0f)); + attached_image_sprite.set_scale(mgl::vec2f(1.0f, 1.0f)); } } } - if(event.type == sf::Event::KeyPressed && navigation_stage == NavigationStage::VIEWING_ATTACHED_IMAGE) { - if(event.key.code == sf::Keyboard::Escape || event.key.code == sf::Keyboard::BackSpace) { + if(event.type == mgl::Event::KeyPressed && navigation_stage == NavigationStage::VIEWING_ATTACHED_IMAGE) { + if(event.key.code == mgl::Keyboard::Escape || event.key.code == mgl::Keyboard::Backspace) { navigation_stage = NavigationStage::VIEWING_COMMENTS; - attached_image_texture.reset(new sf::Texture()); + attached_image_texture.reset(new mgl::Texture()); redraw = true; - } else if(event.key.code == sf::Keyboard::I && event.key.control && !attached_image_url.empty()) { + } else if(event.key.code == mgl::Keyboard::I && event.key.control && !attached_image_url.empty()) { std::vector<Tab> saucenao_tabs; saucenao_tabs.push_back(Tab{create_body(), std::make_unique<SaucenaoPage>(this, attached_image_url, false), nullptr}); page_loop(saucenao_tabs); redraw = true; frame_skip_text_entry = true; - } else if(event.key.code == sf::Keyboard::S && event.key.control) { + } else if(event.key.code == mgl::Keyboard::S && event.key.control) { download_async_gui(attached_image_url, file_manager_start_dir.string(), false); } } @@ -4293,7 +4259,7 @@ namespace QuickMedia { comment_input.set_editable(false); captcha_post_id = std::move(captcha_challenge.challenge_id); solved_captcha_ttl = 0;// captcha_challenge.ttl; // TODO: Support ttl to not have to solve captcha again - captcha_solution_text.setString(""); + captcha_solution_text.set_string(""); captcha_slide = 0.0f; if(captcha_post_id == "noop") { // TODO: Fix for other imageboard than 4chan in the future @@ -4301,16 +4267,16 @@ namespace QuickMedia { navigation_stage = NavigationStage::POSTING_COMMENT; } else { bool failed = false; - sf::Image image; - if(!load_image_from_memory(image, captcha_challenge.img_data.data(), captcha_challenge.img_data.size()) || !captcha_texture.loadFromImage(image)) { + mgl::Image image; + if(!image.load_from_memory((const unsigned char*)captcha_challenge.img_data.data(), captcha_challenge.img_data.size()) || !captcha_texture.load_from_image(image)) { show_notification("QuickMedia", "Failed to load captcha image", Urgency::CRITICAL); failed = true; } captcha_challenge.img_data = std::string(); has_captcha_bg = !failed && !captcha_challenge.bg_data.empty(); - sf::Image bg_Image; - if(has_captcha_bg && (!load_image_from_memory(bg_Image, captcha_challenge.bg_data.data(), captcha_challenge.bg_data.size()) || !captcha_bg_texture.loadFromImage(bg_Image))) { + mgl::Image bg_image; + if(has_captcha_bg && (!bg_image.load_from_memory((const unsigned char*)captcha_challenge.bg_data.data(), captcha_challenge.bg_data.size()) || !captcha_bg_texture.load_from_image(bg_image))) { show_notification("QuickMedia", "Failed to load captcha image", Urgency::CRITICAL); failed = true; } @@ -4320,9 +4286,9 @@ namespace QuickMedia { navigation_stage = NavigationStage::VIEWING_COMMENTS; } else { navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA; - captcha_sprite.setTexture(captcha_texture, true); + captcha_sprite.set_texture(&captcha_texture); if(has_captcha_bg) - captcha_bg_sprite.setTexture(captcha_bg_texture, true); + captcha_bg_sprite.set_texture(&captcha_bg_texture); } } } else if(task_result == TaskResult::CANCEL) { @@ -4351,13 +4317,12 @@ namespace QuickMedia { redraw = true; } } else { - file_to_upload_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(selected_file_for_upload, true, sf::Vector2i(logo_size.x, logo_size.y * 4)); + file_to_upload_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(selected_file_for_upload, true, mgl::vec2i(logo_size.x, logo_size.y * 4)); } - if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && file_to_upload_thumbnail_data->image->getSize().x > 0 && file_to_upload_thumbnail_data->image->getSize().y > 0) { - if(!file_to_upload_thumbnail_data->texture.loadFromImage(*file_to_upload_thumbnail_data->image)) + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && file_to_upload_thumbnail_data->image->get_size().x > 0 && file_to_upload_thumbnail_data->image->get_size().y > 0) { + if(!file_to_upload_thumbnail_data->texture.load_from_image(*file_to_upload_thumbnail_data->image)) fprintf(stderr, "Warning: failed to load texture for attached file\n"); - file_to_upload_thumbnail_data->texture.setSmooth(true); //room_avatar_thumbnail_data->texture.generateMipmap(); file_to_upload_thumbnail_data->image.reset(); file_to_upload_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE; @@ -4365,18 +4330,18 @@ namespace QuickMedia { } if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && !sprite_applied_texture) { - file_to_upload_sprite.setTexture(file_to_upload_thumbnail_data->texture, true); + file_to_upload_sprite.set_texture(&file_to_upload_thumbnail_data->texture); sprite_applied_texture = true; - sf::Vector2f texture_size_f(file_to_upload_thumbnail_data->texture.getSize().x, file_to_upload_thumbnail_data->texture.getSize().y); - sf::Vector2f image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, logo_size)); - file_to_upload_sprite.setScale(image_scale); + mgl::vec2f texture_size_f(file_to_upload_thumbnail_data->texture.get_size().x, file_to_upload_thumbnail_data->texture.get_size().y); + mgl::vec2f image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, logo_size.x)); + file_to_upload_sprite.set_scale(image_scale); redraw = true; } float chat_input_height_full_images = logo_size.y; if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { - const float file_to_upload_height = std::floor(logo_file_to_upload_spacing + file_to_upload_sprite.getTexture()->getSize().y * file_to_upload_sprite.getScale().y); + const float file_to_upload_height = std::floor(logo_file_to_upload_spacing + file_to_upload_sprite.get_texture()->get_size().y * file_to_upload_sprite.get_scale().y); chat_input_height_full_images += file_to_upload_height; } chat_input_height_full = chat_input_padding_y + std::max(comment_input.get_height(), chat_input_height_full_images) + chat_input_padding_y; @@ -4395,59 +4360,59 @@ namespace QuickMedia { const float body_width = window_size.x; - comment_input_shade.setSize(sf::Vector2f(window_size.x, chat_input_height_full)); - comment_input_shade.setPosition(0.0f, 0.0f); + comment_input_shade.set_size(mgl::vec2f(window_size.x, chat_input_height_full)); + comment_input_shade.set_position(mgl::vec2f(0.0f, 0.0f)); - body_pos = sf::Vector2f(0.0f, comment_input_shade.getSize().y); - body_size = sf::Vector2f(body_width, window_size.y - comment_input_shade.getSize().y); + body_pos = mgl::vec2f(0.0f, comment_input_shade.get_size().y); + body_size = mgl::vec2f(body_width, window_size.y - comment_input_shade.get_size().y); - logo_sprite.setPosition(logo_padding_x, chat_input_padding_y); - file_to_upload_sprite.setPosition(logo_sprite.getPosition() + sf::Vector2f(0.0f, logo_size.y + logo_file_to_upload_spacing)); + logo_sprite.set_position(mgl::vec2f(logo_padding_x, chat_input_padding_y)); + file_to_upload_sprite.set_position(logo_sprite.get_position() + mgl::vec2f(0.0f, logo_size.y + logo_file_to_upload_spacing)); } //comment_input.update(); window.clear(get_theme().background_color); - if(navigation_stage == NavigationStage::SOLVING_POST_CAPTCHA && captcha_texture.getNativeHandle() != 0) { + if(navigation_stage == NavigationStage::SOLVING_POST_CAPTCHA && captcha_texture.is_valid()) { const float slide_speed = 0.5f; - const bool window_has_focus = window.hasFocus(); - if(window_has_focus && sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + const bool window_has_focus = window.has_focus(); + if(window_has_focus && window.is_key_pressed(mgl::Keyboard::Left)) { captcha_slide -= (slide_speed * frame_elapsed_time_sec); if(captcha_slide < 0.0f) captcha_slide = 0.0f; - } else if(window_has_focus && sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + } else if(window_has_focus && window.is_key_pressed(mgl::Keyboard::Right)) { captcha_slide += (slide_speed * frame_elapsed_time_sec); if(captcha_slide > 1.0f) captcha_slide = 1.0f; } - sf::Vector2f content_size = window_size; + mgl::vec2f content_size = window_size.to_vec2f(); int image_height = 0; - sf::Vector2u captcha_texture_size = captcha_texture.getSize(); - sf::Vector2f captcha_texture_size_f(captcha_texture_size.x, captcha_texture_size.y); + mgl::vec2i captcha_texture_size = captcha_texture.get_size(); + mgl::vec2f captcha_texture_size_f(captcha_texture_size.x, captcha_texture_size.y); auto image_scale = get_ratio(captcha_texture_size_f, clamp_to_size(captcha_texture_size_f, content_size)); - captcha_sprite.setScale(image_scale); + captcha_sprite.set_scale(image_scale); auto captcha_image_size = captcha_texture_size_f; captcha_image_size.x *= image_scale.x; captcha_image_size.y *= image_scale.y; - captcha_sprite.setPosition(vec2f_floor(content_size.x * 0.5f - captcha_image_size.x * 0.5f, content_size.y * 0.5f - captcha_image_size.y * 0.5f)); + captcha_sprite.set_position(vec2f_floor(content_size.x * 0.5f - captcha_image_size.x * 0.5f, content_size.y * 0.5f - captcha_image_size.y * 0.5f)); image_height = (int)captcha_image_size.y; - if(has_captcha_bg && captcha_bg_texture.getNativeHandle() != 0) { - sf::Vector2f content_size = window_size; + if(has_captcha_bg && captcha_bg_texture.is_valid()) { + mgl::vec2f content_size = window_size.to_vec2f(); - sf::Vector2u captcha_bg_texture_size = captcha_bg_texture.getSize(); - sf::Vector2f captcha_bg_texture_size_f(captcha_bg_texture_size.x, captcha_bg_texture_size.y); + mgl::vec2i captcha_bg_texture_size = captcha_bg_texture.get_size(); + mgl::vec2f captcha_bg_texture_size_f(captcha_bg_texture_size.x, captcha_bg_texture_size.y); auto image_scale = get_ratio(captcha_bg_texture_size_f, clamp_to_size(captcha_bg_texture_size_f, content_size)); - captcha_bg_sprite.setScale(image_scale); + captcha_bg_sprite.set_scale(image_scale); auto image_size = captcha_bg_texture_size_f; image_size.x *= image_scale.x; image_size.y *= image_scale.y; const float width_diff = image_size.x - captcha_image_size.x; - captcha_bg_sprite.setPosition(vec2f_floor(captcha_sprite.getPosition().x + width_diff*1.0f - captcha_slide*(width_diff + width_diff*2.0f), captcha_sprite.getPosition().y)); + captcha_bg_sprite.set_position(vec2f_floor(captcha_sprite.get_position().x + width_diff*1.0f - captcha_slide*(width_diff + width_diff*2.0f), captcha_sprite.get_position().y)); window.draw(captcha_bg_sprite); image_height = std::max(image_height, (int)image_size.y); @@ -4456,64 +4421,64 @@ namespace QuickMedia { window.draw(captcha_sprite); // TODO: Cut off ends with sf::View instead - sf::RectangleShape cut_off_rectangle(captcha_image_size); - cut_off_rectangle.setFillColor(get_theme().background_color); - cut_off_rectangle.setPosition(captcha_sprite.getPosition() - sf::Vector2f(cut_off_rectangle.getSize().x, 0.0f)); + mgl::Rectangle cut_off_rectangle(captcha_image_size); + cut_off_rectangle.set_color(get_theme().background_color); + cut_off_rectangle.set_position(captcha_sprite.get_position() - mgl::vec2f(cut_off_rectangle.get_size().x, 0.0f)); window.draw(cut_off_rectangle); - cut_off_rectangle.setPosition(captcha_sprite.getPosition() + sf::Vector2f(captcha_image_size.x, 0.0f)); + cut_off_rectangle.set_position(captcha_sprite.get_position() + mgl::vec2f(captcha_image_size.x, 0.0f)); window.draw(cut_off_rectangle); const float captcha_slide_bg_height = std::floor(20.0f * get_config().scale); - captcha_slide_bg.set_size(sf::Vector2f(captcha_image_size.x, captcha_slide_bg_height)); - captcha_slide_bg.set_position(sf::Vector2f(captcha_sprite.getPosition().x, captcha_sprite.getPosition().y + image_height + 10.0f)); + captcha_slide_bg.set_size(mgl::vec2f(captcha_image_size.x, captcha_slide_bg_height)); + captcha_slide_bg.set_position(mgl::vec2f(captcha_sprite.get_position().x, captcha_sprite.get_position().y + image_height + 10.0f)); - const sf::Vector2f captcha_slide_fg_size = captcha_slide_bg.get_size() - sf::Vector2f(captcha_slide_padding_x * 2.0f, captcha_slide_padding_y * 2.0f); + const mgl::vec2f captcha_slide_fg_size = captcha_slide_bg.get_size() - mgl::vec2f(captcha_slide_padding_x * 2.0f, captcha_slide_padding_y * 2.0f); captcha_slide_fg.set_size(vec2f_floor(captcha_slide_fg_size.x * captcha_slide, captcha_slide_fg_size.y)); - captcha_slide_fg.set_position(captcha_slide_bg.get_position() + sf::Vector2f(captcha_slide_padding_x, captcha_slide_padding_y)); + captcha_slide_fg.set_position(captcha_slide_bg.get_position() + mgl::vec2f(captcha_slide_padding_x, captcha_slide_padding_y)); if(has_captcha_bg) { captcha_slide_bg.draw(window); captcha_slide_fg.draw(window); } - captcha_solution_text.setPosition( - sf::Vector2f( - std::floor(window_size.x * 0.5f - captcha_solution_text.getLocalBounds().width * 0.5f), + captcha_solution_text.set_position( + mgl::vec2f( + std::floor(window_size.x * 0.5f - captcha_solution_text.get_bounds().size.x * 0.5f), std::floor(captcha_slide_bg.get_position().y + captcha_slide_bg.get_size().y + 10.0f))); window.draw(captcha_solution_text); } else if(navigation_stage == NavigationStage::VIEWING_ATTACHED_IMAGE) { - if(attached_image_texture->getNativeHandle() != 0) { + if(attached_image_texture->is_valid()) { if(image_control.moved) { - attached_image_sprite.setOrigin(floor(image_control.offset)); - attached_image_sprite.setScale(image_control.zoom, image_control.zoom); - attached_image_sprite.setPosition(floor(window_size * 0.5f)); + attached_image_sprite.set_origin(floor(image_control.offset)); + attached_image_sprite.set_scale(mgl::vec2f(image_control.zoom, image_control.zoom)); + attached_image_sprite.set_position(floor(window_size.to_vec2f() * 0.5f)); window.draw(attached_image_sprite); } else { - auto content_size = window_size; - sf::Vector2u texture_size = attached_image_texture->getSize(); - sf::Vector2f texture_size_f(texture_size.x, texture_size.y); + auto content_size = window_size.to_vec2f(); + mgl::vec2i texture_size = attached_image_texture->get_size(); + mgl::vec2f texture_size_f(texture_size.x, texture_size.y); auto image_scale = get_ratio(texture_size_f, clamp_to_size(texture_size_f, content_size)); image_control.zoom = std::min(image_scale.x, image_scale.y); - attached_image_sprite.setScale(image_scale); + attached_image_sprite.set_scale(image_scale); auto image_size = texture_size_f; image_size.x *= image_scale.x; image_size.y *= image_scale.y; - attached_image_sprite.setPosition(vec2f_floor(content_size.x * 0.5f - image_size.x * 0.5f, content_size.y * 0.5f - image_size.y * 0.5f)); + attached_image_sprite.set_position(vec2f_floor(content_size.x * 0.5f - image_size.x * 0.5f, content_size.y * 0.5f - image_size.y * 0.5f)); window.draw(attached_image_sprite); } } else { - sf::RectangleShape rect(sf::Vector2f(640.0f, 480.0f)); - rect.setFillColor(get_theme().image_loading_background_color); - auto content_size = window_size; - auto rect_size = clamp_to_size(rect.getSize(), content_size); - rect.setSize(rect_size); - rect.setPosition(vec2f_floor(content_size.x * 0.5f - rect_size.x * 0.5f, content_size.y * 0.5f - rect_size.y * 0.5f)); + mgl::Rectangle rect(mgl::vec2f(640.0f, 480.0f)); + rect.set_color(get_theme().image_loading_background_color); + auto content_size = window_size.to_vec2f(); + auto rect_size = clamp_to_size(rect.get_size(), content_size); + rect.set_size(rect_size); + rect.set_position(vec2f_floor(content_size.x * 0.5f - rect_size.x * 0.5f, content_size.y * 0.5f - rect_size.y * 0.5f)); window.draw(rect); - load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); - load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); + load_sprite.set_position(mgl::vec2f(window_size.x * 0.5f, window_size.y * 0.5f)); + load_sprite.set_rotation(load_sprite_timer.get_elapsed_time_seconds() * 400.0); window.draw(load_sprite); } } else if(navigation_stage == NavigationStage::REPLYING) { @@ -4535,16 +4500,16 @@ namespace QuickMedia { if((navigation_stage == NavigationStage::REPLYING || navigation_stage == NavigationStage::VIEWING_COMMENTS) && has_post_timeout) { int64_t time_left_until_post_again = seconds_until_post_again - (time(nullptr) - last_posted_time); if(time_left_until_post_again > 0) { - sf::RectangleShape time_left_bg(comment_input_shade.getSize()); - time_left_bg.setPosition(comment_input_shade.getPosition()); - time_left_bg.setFillColor(sf::Color(0, 0, 0, 100)); + mgl::Rectangle time_left_bg(comment_input_shade.get_size()); + time_left_bg.set_position(comment_input_shade.get_position()); + time_left_bg.set_color(mgl::Color(0, 0, 0, 100)); window.draw(time_left_bg); - sf::Text time_left_text("Wait " + std::to_string(time_left_until_post_again) + " second(s) before posting again", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(18 * get_config().scale * get_config().font_scale)); - time_left_text.setPosition(time_left_bg.getPosition() + - sf::Vector2f( - std::floor(time_left_bg.getSize().x * 0.5f - time_left_text.getLocalBounds().width * 0.5f), - std::floor(time_left_bg.getSize().y * 0.5f - time_left_text.getLocalBounds().height * 0.5f))); + mgl::Text time_left_text("Wait " + std::to_string(time_left_until_post_again) + " second(s) before posting again", *FontLoader::get_font(FontLoader::FontType::LATIN, 18 * get_config().scale * get_config().font_scale)); + time_left_text.set_position(time_left_bg.get_position() + + mgl::vec2f( + std::floor(time_left_bg.get_size().x * 0.5f - time_left_text.get_bounds().size.x * 0.5f), + std::floor(time_left_bg.get_size().y * 0.5f - time_left_text.get_bounds().size.y * 0.5f))); window.draw(time_left_text); } } @@ -4565,9 +4530,9 @@ namespace QuickMedia { SearchBar *inputs[num_inputs] = { &login_input, &password_input, &homeserver_input }; int focused_input = 0; - RoundedRectangle background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, &rounded_rectangle_shader); + RoundedRectangle background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, &rounded_rectangle_shader); - auto text_submit_callback = [this, inputs](const sf::String&) { + auto text_submit_callback = [this, inputs](const std::string&) { for(int i = 0; i < num_inputs; ++i) { if(inputs[i]->get_text().empty()) { show_notification("QuickMedia", "All fields need to be filled in", Urgency::CRITICAL); @@ -4603,26 +4568,24 @@ namespace QuickMedia { const float padding_y = std::floor(20.0f * get_config().scale); const float spacing_y = std::floor(20.0f * get_config().scale); - sf::Vector2f body_pos; - sf::Vector2f body_size; + mgl::vec2f body_pos; + mgl::vec2f body_size; bool redraw = true; - sf::Event event; + mgl::Event event; - while (current_page == PageType::CHAT_LOGIN && window.isOpen()) { - while (window.pollEvent(event)) { + while (current_page == PageType::CHAT_LOGIN && window.is_open()) { + while (window.poll_event(event)) { common_event_handler(event); event_idle_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); redraw = true; idle_active_handler(); - } else if(event.type == sf::Event::GainedFocus) { + } else if(event.type == mgl::Event::GainedFocus) { redraw = true; - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Tab) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Tab) { for(int i = 0; i < num_inputs; ++i) { inputs[i]->caret_visible = false; } @@ -4630,7 +4593,7 @@ namespace QuickMedia { inputs[focused_input]->caret_visible = true; idle_active_handler(); } - inputs[focused_input]->on_event(event); + inputs[focused_input]->on_event(window, event); } update_idle_state(); handle_x11_events(); @@ -4641,12 +4604,12 @@ namespace QuickMedia { if(redraw) { redraw = false; get_body_dimensions(window_size, nullptr, body_pos, body_size); - background.set_size(sf::Vector2f( - std::min(window_size.x, std::max(640.0f, window_size.x * 0.5f)), + background.set_size(mgl::vec2f( + std::min((float)window_size.x, std::max(640.0f, window_size.x * 0.5f)), num_inputs * inputs[0]->getBottomWithoutShadow() + padding_y * 2.0f + spacing_y * std::max(0, num_inputs - 1))); - background.set_position(window_size * 0.5f - background.get_size() * 0.5f); + background.set_position(window_size.to_vec2f() * 0.5f - background.get_size() * 0.5f); - sf::Vector2f pos = background.get_position() + sf::Vector2f(padding_x, padding_y); + mgl::vec2f pos = background.get_position() + mgl::vec2f(padding_x, padding_y); for(int i = 0; i < num_inputs; ++i) { inputs[i]->set_position(pos); pos.y += inputs[i]->getBottomWithoutShadow() + spacing_y; @@ -4657,7 +4620,7 @@ namespace QuickMedia { background.draw(window); for(int i = 0; i < num_inputs; ++i) { inputs[i]->update(); - inputs[i]->draw(window, background.get_size() - sf::Vector2f(padding_x * 2.0f, 0.0f), false); + inputs[i]->draw(window, background.get_size() - mgl::vec2f(padding_x * 2.0f, 0.0f), false); } AsyncImageLoader::get_instance().update(); window.display(); @@ -4669,7 +4632,7 @@ namespace QuickMedia { AsyncTask<BodyItems> future; }; - static const sf::Vector2i CHAT_MESSAGE_THUMBNAIL_MAX_SIZE(600, 337); + static const mgl::vec2i CHAT_MESSAGE_THUMBNAIL_MAX_SIZE(600, 337); // TODO: Optimize static std::shared_ptr<BodyItem> find_body_item_by_event_id(const std::shared_ptr<BodyItem> *body_items, size_t num_body_items, const std::string &event_id, size_t *index_result = nullptr) { @@ -4852,7 +4815,7 @@ namespace QuickMedia { show_notification("QuickMedia", "Bug: current room empty", Urgency::CRITICAL); abort(); } - window.setTitle("QuickMedia - matrix - " + current_room->get_name()); + window.set_title(("QuickMedia - matrix - " + current_room->get_name()).c_str()); auto video_page = std::make_unique<MatrixVideoPage>(this); bool move_room = false; @@ -4881,7 +4844,7 @@ namespace QuickMedia { tab.body->show_drop_shadow = false; } - Tabs ui_tabs(&rounded_rectangle_shader, is_touch_enabled() ? sf::Color::Transparent : get_theme().background_color); + Tabs ui_tabs(&rounded_rectangle_shader, is_touch_enabled() ? mgl::Color(0, 0, 0, 0) : get_theme().background_color); const int PINNED_TAB_INDEX = ui_tabs.add_tab("Pinned messages (0)", tabs[0].body.get()); const int MESSAGES_TAB_INDEX = ui_tabs.add_tab("Messages", tabs[1].body.get()); const int USERS_TAB_INDEX = ui_tabs.add_tab("Users (0)", tabs[2].body.get()); @@ -4894,14 +4857,13 @@ namespace QuickMedia { bool redraw = true; - sf::Clock read_marker_timer; - const sf::Int32 read_marker_timeout_ms_default = 3000; - sf::Int32 read_marker_timeout_ms = 0; + mgl::Clock read_marker_timer; + double read_marker_timeout_sec = 0; AsyncTask<void> set_read_marker_future; bool setting_read_marker = false; - sf::Clock start_typing_timer; + mgl::Clock start_typing_timer; const double typing_timeout_seconds = 5.0; bool typing = false; @@ -4936,7 +4898,7 @@ namespace QuickMedia { } }; - bool is_window_focused = window.hasFocus(); + bool is_window_focused = window.has_focus(); enum class ChatState { NAVIGATING, @@ -4950,26 +4912,26 @@ namespace QuickMedia { ChatState chat_state = ChatState::NAVIGATING; std::shared_ptr<BodyItem> currently_operating_on_item; - sf::Text replying_to_text("Replying to:", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(18 * get_config().scale * get_config().font_scale)); + mgl::Text replying_to_text("Replying to:", *FontLoader::get_font(FontLoader::FontType::LATIN, 18 * get_config().scale * get_config().font_scale)); - sf::Sprite logo_sprite(plugin_logo); - logo_sprite.setScale(0.8f * get_config().scale, 0.8f * get_config().scale); - sf::Vector2f logo_size(plugin_logo.getSize().x * logo_sprite.getScale().x, plugin_logo.getSize().y * logo_sprite.getScale().y); + mgl::Sprite logo_sprite(&plugin_logo); + logo_sprite.set_scale(mgl::vec2f(0.8f * get_config().scale, 0.8f * get_config().scale)); + mgl::vec2f logo_size(plugin_logo.get_size().x * logo_sprite.get_scale().x, plugin_logo.get_size().y * logo_sprite.get_scale().y); const float room_name_text_height = std::floor(18.0f * get_config().scale * get_config().font_scale); - sf::Text room_name_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD), room_name_text_height); + mgl::Text room_name_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, room_name_text_height)); const float room_name_text_padding_y = std::floor(10.0f * get_config().scale); const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f; const float room_avatar_height = 32.0f; const float room_topic_text_height = std::floor(12.0f * get_config().scale * get_config().font_scale); - sf::Text room_topic_text("", *FontLoader::get_font(FontLoader::FontType::LATIN), room_topic_text_height); - room_topic_text.setFillColor(get_theme().faded_text_color); + mgl::Text room_topic_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, room_topic_text_height)); + room_topic_text.set_color(get_theme().faded_text_color); - sf::Text room_label(matrix_chat_page->rooms_page->get_title(), *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD), std::floor(18 * get_config().scale * get_config().font_scale)); - room_label.setPosition(15.0f, room_name_text_padding_y + 4.0f); + mgl::Text room_label(matrix_chat_page->rooms_page->get_title(), *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, 18 * get_config().scale * get_config().font_scale)); + room_label.set_position(mgl::vec2f(15.0f, room_name_text_padding_y + 4.0f)); - sf::Sprite room_avatar_sprite; + mgl::Sprite room_avatar_sprite; auto room_avatar_thumbnail_data = std::make_shared<ThumbnailData>(); bool draw_room_list = show_room_side_panel; @@ -5205,10 +5167,10 @@ namespace QuickMedia { std::string room_topic = current_room->get_topic(); string_replace_all(room_name, '\n', ' '); string_replace_all(room_topic, '\n', ' '); - room_name_text.setString(sf::String::fromUtf8(room_name.begin(), room_name.end())); - room_topic_text.setString(sf::String::fromUtf8(room_topic.begin(), room_topic.end())); + room_name_text.set_string(std::move(room_name)); + room_topic_text.set_string(std::move(room_topic)); - read_marker_timeout_ms = 0; + read_marker_timeout_sec = 0; redraw = true; Entry chat_input("Press i to begin writing a message...", &rounded_rectangle_shader); @@ -5284,10 +5246,10 @@ namespace QuickMedia { }; struct Mention { - sf::Clock filter_timer; + mgl::Clock filter_timer; bool visible = false; bool filter_updated = false; - sf::String filter; + std::string filter; Body *users_tab_body = nullptr; void show() { @@ -5303,44 +5265,42 @@ namespace QuickMedia { users_tab_body->clear_cache(); } - void handle_event(const sf::Event &event) { + void handle_event(const mgl::Event &event) { if(visible) { - if(event.type == sf::Event::TextEntered) { + if(event.type == mgl::Event::TextEntered) { filter_timer.restart(); - if(event.text.unicode > 32) { - filter += event.text.unicode; + if(event.text.codepoint > 32) { + filter.append(event.text.str, event.text.size); filter_updated = true; - } else if(event.text.unicode == ' ' || event.text.unicode == '\t') { + } else if(event.text.codepoint == ' ' || event.text.codepoint == '\t') { hide(); } - } else if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) { + } else if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Up || (event.key.control && event.key.code == mgl::Keyboard::K)) { users_tab_body->select_previous_item(true); - } else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) { + } else if(event.key.code == mgl::Keyboard::Down || (event.key.control && event.key.code == mgl::Keyboard::J)) { users_tab_body->select_next_item(true); - } else if(event.key.code == sf::Keyboard::Enter && event.key.shift) { + } else if(event.key.code == mgl::Keyboard::Enter && event.key.shift) { hide(); - } else if(event.key.code == sf::Keyboard::Backspace) { - if(filter.getSize() == 0) { + } else if(event.key.code == mgl::Keyboard::Backspace) { + if(filter.empty()) { hide(); } else { - filter.erase(filter.getSize() - 1, 1); + filter.erase(filter.size() - 1, 1); filter_updated = true; } } } } - if(event.type == sf::Event::TextEntered && event.text.unicode == '@' && !visible) + if(event.type == mgl::Event::TextEntered && event.text.codepoint == '@' && !visible) show(); } void update() { - if(visible && filter_updated && filter_timer.getElapsedTime().asMilliseconds() > 50) { + if(visible && filter_updated && filter_timer.get_elapsed_time_seconds() > 0.05) { filter_updated = false; - // TODO: Use std::string instead of sf::String - auto u8 = filter.toUtf8(); - users_tab_body->filter_search_fuzzy(*(std::string*)&u8); + users_tab_body->filter_search_fuzzy(filter); users_tab_body->select_first_item(); } } @@ -5360,7 +5320,7 @@ namespace QuickMedia { if(!str_to_append.empty()) str_to_append.erase(0, 1); str_to_append += " "; - chat_input.replace(chat_input.get_caret_index() - mention.filter.getSize(), mention.filter.getSize(), sf::String::fromUtf8(str_to_append.begin(), str_to_append.end())); + chat_input.replace(chat_input.get_caret_index() - mention.filter.size(), mention.filter.size(), str_to_append); mention.hide(); } return false; @@ -5690,12 +5650,12 @@ namespace QuickMedia { return false; }; - sf::Vector2f body_pos; - sf::Vector2f body_size; - sf::Event event; + mgl::vec2f body_pos; + mgl::vec2f body_size; + mgl::Event event; const float gradient_height = 5.0f; - sf::Vertex gradient_points[4]; + mgl::Vertex gradient_points[4]; double gradient_inc = 0; std::string before_token; @@ -5704,15 +5664,15 @@ namespace QuickMedia { bool fetched_enough_messages_bottom = false; bool has_unread_messages = false; - sf::RectangleShape more_messages_below_rect; - more_messages_below_rect.setFillColor(get_theme().new_items_alert_color); + mgl::Rectangle more_messages_below_rect; + more_messages_below_rect.set_color(get_theme().new_items_alert_color); - sf::RectangleShape chat_input_shade; - chat_input_shade.setFillColor(get_theme().shade_color); + mgl::Rectangle chat_input_shade; + chat_input_shade.set_color(get_theme().shade_color); float tab_vertical_offset = 0.0f; - sf::Clock frame_timer; + mgl::Clock frame_timer; float prev_chat_height = chat_input.get_height(); float chat_input_height_full = 0.0f; @@ -6103,9 +6063,9 @@ namespace QuickMedia { goto chat_page_end; } - while (current_page == PageType::CHAT && window.isOpen() && !move_room) { - sf::Int32 frame_time_ms = frame_timer.restart().asMilliseconds(); - while (window.pollEvent(event)) { + while (current_page == PageType::CHAT && window.is_open() && !move_room) { + int32_t frame_time_ms = frame_timer.restart() * 1000.0; + while (window.poll_event(event)) { common_event_handler(event); const int selected_tab = ui_tabs.get_selected(); @@ -6123,8 +6083,8 @@ namespace QuickMedia { base_event_handler(event, PageType::EXIT, tabs[selected_tab].body.get(), nullptr, false, false); event_idle_handler(event); if(!frame_skip_text_entry) { - if(!mention.visible || event.type != sf::Event::KeyPressed || (event.key.code != sf::Keyboard::Up && event.key.code != sf::Keyboard::Down && event.key.code != sf::Keyboard::Left && event.key.code != sf::Keyboard::Right)) - chat_input.process_event(event); + if(!mention.visible || event.type != mgl::Event::KeyPressed || (event.key.code != mgl::Keyboard::Up && event.key.code != mgl::Keyboard::Down && event.key.code != mgl::Keyboard::Left && event.key.code != mgl::Keyboard::Right)) + chat_input.process_event(window, event); if(chat_input.is_editable()) mention.handle_event(event); } @@ -6134,52 +6094,50 @@ namespace QuickMedia { idle_active_handler(); } - if(event.type == sf::Event::KeyPressed && event.key.alt && (chat_state == ChatState::NAVIGATING || chat_state == ChatState::URL_SELECTION)) { - if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) { + if(event.type == mgl::Event::KeyPressed && event.key.alt && (chat_state == ChatState::NAVIGATING || chat_state == ChatState::URL_SELECTION)) { + if(event.key.code == mgl::Keyboard::Up || (event.key.control && event.key.code == mgl::Keyboard::K)) { matrix_chat_page->rooms_page->body->select_previous_item(true); move_room = true; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::Down || (event.key.control && event.key.code == sf::Keyboard::J)) { + } else if(event.key.code == mgl::Keyboard::Down || (event.key.control && event.key.code == mgl::Keyboard::J)) { matrix_chat_page->rooms_page->body->select_next_item(true); move_room = true; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::PageUp) { + } else if(event.key.code == mgl::Keyboard::PageUp) { matrix_chat_page->rooms_page->body->select_previous_page(); move_room = true; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::PageDown) { + } else if(event.key.code == mgl::Keyboard::PageDown) { matrix_chat_page->rooms_page->body->select_next_page(); move_room = true; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::Home) { + } else if(event.key.code == mgl::Keyboard::Home) { matrix_chat_page->rooms_page->body->select_first_item(false); move_room = true; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::End) { + } else if(event.key.code == mgl::Keyboard::End) { matrix_chat_page->rooms_page->body->select_last_item(); move_room = true; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::Escape) { + } else if(event.key.code == mgl::Keyboard::Escape) { move_room = false; goto chat_page_end; } continue; } - if(event.type == sf::Event::GainedFocus) { + if(event.type == mgl::Event::GainedFocus) { is_window_focused = true; redraw = true; - } else if(event.type == sf::Event::LostFocus) { + } else if(event.type == mgl::Event::LostFocus) { is_window_focused = false; - } else if(event.type == sf::Event::Resized) { + } else if(event.type == mgl::Event::Resized) { redraw = true; idle_active_handler(); - } else if(event.type == sf::Event::GainedFocus) { - redraw = true; - } else if(event.type == sf::Event::KeyPressed && chat_state == ChatState::NAVIGATING) { - if(event.key.code == sf::Keyboard::Escape) { + } else if(event.type == mgl::Event::KeyPressed && chat_state == ChatState::NAVIGATING) { + if(event.key.code == mgl::Keyboard::Escape) { goto chat_page_end; - } else if(event.key.code == sf::Keyboard::I && event.key.control) { + } else if(event.key.code == mgl::Keyboard::I && event.key.control) { BodyItem *selected_item = tabs[selected_tab].body->get_selected(); if(selected_item && selected_item->url.empty()) selected_item = selected_item->embedded_item.get(); @@ -6206,13 +6164,13 @@ namespace QuickMedia { } if((selected_tab == MESSAGES_TAB_INDEX || selected_tab == PINNED_TAB_INDEX) && !frame_skip_text_entry) { - if(event.key.code == sf::Keyboard::Enter) { + if(event.key.code == mgl::Keyboard::Enter) { BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { if(!display_url_or_image(selected)) display_url_or_image(selected->embedded_item.get()); } - } else if(event.key.code == sf::Keyboard::S && event.key.control) { + } else if(event.key.code == mgl::Keyboard::S && event.key.control) { BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { if(!download_selected_item(selected)) @@ -6221,35 +6179,32 @@ namespace QuickMedia { } } - if(event.key.control && event.key.code == sf::Keyboard::C) { + if(event.key.control && event.key.code == mgl::Keyboard::C) { BodyItem *selected = tabs[selected_tab].body->get_selected(); - if(selected) { - auto desc = selected->get_description(); - sf::Clipboard::setString(sf::String::fromUtf8(desc.begin(), desc.end())); - } + if(selected) + set_clipboard(selected->get_description()); } if(selected_tab == MESSAGES_TAB_INDEX) { - if(event.key.code == sf::Keyboard::U) { + if(event.key.code == mgl::Keyboard::U) { frame_skip_text_entry = true; new_page = PageType::FILE_MANAGER; chat_input.set_editable(false); } - if(event.key.code == sf::Keyboard::I && !event.key.control) { + if(event.key.code == mgl::Keyboard::I && !event.key.control) { frame_skip_text_entry = true; chat_input.set_editable(true); chat_state = ChatState::TYPING_MESSAGE; } - if(event.key.control && event.key.code == sf::Keyboard::V) { + if(event.key.control && event.key.code == mgl::Keyboard::V) { frame_skip_text_entry = true; // TODO: Upload multiple files. - auto clipboard = sf::Clipboard::getString().toUtf8(); - upload_file(std::string(clipboard.begin(), clipboard.end())); + upload_file(window.get_clipboard()); } - if(event.key.code == sf::Keyboard::R && tabs[selected_tab].body->get_selected_shared()) { + if(event.key.code == mgl::Keyboard::R && tabs[selected_tab].body->get_selected_shared()) { std::shared_ptr<BodyItem> selected = tabs[selected_tab].body->get_selected_shared(); Message *selected_message = static_cast<Message*>(selected->userdata); const bool go_to_replied_message = event.key.control; @@ -6272,20 +6227,20 @@ namespace QuickMedia { chat_state = ChatState::REPLYING; currently_operating_on_item = selected; chat_input.set_editable(true); - replying_to_text.setString("Replying to:"); + replying_to_text.set_string("Replying to:"); frame_skip_text_entry = true; } } } } - if(event.key.code == sf::Keyboard::B && event.key.control) { + if(event.key.code == mgl::Keyboard::B && event.key.control) { // Reload room, goes to latest message l0l move_room = true; goto chat_page_end; } - if(event.key.code == sf::Keyboard::E) { + if(event.key.code == mgl::Keyboard::E) { frame_skip_text_entry = true; std::shared_ptr<BodyItem> selected = tabs[selected_tab].body->get_selected_shared(); if(selected) { @@ -6305,7 +6260,7 @@ namespace QuickMedia { chat_input.set_editable(true); chat_input.set_text(selected->get_description()); // TODO: Description? it may change in the future, in which case this should be edited chat_input.move_caret_to_end(); - replying_to_text.setString("Editing message:"); + replying_to_text.set_string("Editing message:"); } } } else { @@ -6314,7 +6269,7 @@ namespace QuickMedia { } } - if(event.key.control && event.key.code == sf::Keyboard::D && !chat_input.is_editable()) { + if(event.key.control && event.key.code == mgl::Keyboard::D && !chat_input.is_editable()) { frame_skip_text_entry = true; BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { @@ -6342,7 +6297,7 @@ namespace QuickMedia { } } - if(event.key.control && event.key.code == sf::Keyboard::P && !chat_input.is_editable()) { + if(event.key.control && event.key.code == mgl::Keyboard::P && !chat_input.is_editable()) { frame_skip_text_entry = true; BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { @@ -6363,7 +6318,7 @@ namespace QuickMedia { } } } else if(selected_tab == PINNED_TAB_INDEX) { - if(event.key.control && event.key.code == sf::Keyboard::D && !chat_input.is_editable()) { + if(event.key.control && event.key.code == mgl::Keyboard::D && !chat_input.is_editable()) { frame_skip_text_entry = true; BodyItem *selected = tabs[selected_tab].body->get_selected(); if(selected) { @@ -6379,7 +6334,7 @@ namespace QuickMedia { } } - if(event.key.code == sf::Keyboard::R && event.key.control && tabs[selected_tab].body->get_selected_shared()) { + if(event.key.code == mgl::Keyboard::R && event.key.control && tabs[selected_tab].body->get_selected_shared()) { std::shared_ptr<BodyItem> selected = tabs[selected_tab].body->get_selected_shared(); PinnedEventData *selected_event_data = static_cast<PinnedEventData*>(selected->userdata); if(selected_event_data && !selected_event_data->event_id.empty()) { @@ -6388,11 +6343,11 @@ namespace QuickMedia { } } } - } else if(event.type == sf::Event::KeyPressed && chat_state == ChatState::URL_SELECTION) { - if(event.key.code == sf::Keyboard::Escape) { + } else if(event.type == mgl::Event::KeyPressed && chat_state == ChatState::URL_SELECTION) { + if(event.key.code == mgl::Keyboard::Escape) { url_selection_body.clear_items(); chat_state = ChatState::NAVIGATING; - } else if(event.key.code == sf::Keyboard::Enter) { + } else if(event.key.code == mgl::Keyboard::Enter) { BodyItem *selected_item = url_selection_body.get_selected(); if(!selected_item) continue; @@ -6402,9 +6357,9 @@ namespace QuickMedia { if((chat_state == ChatState::TYPING_MESSAGE || chat_state == ChatState::REPLYING || chat_state == ChatState::EDITING) && selected_tab == MESSAGES_TAB_INDEX && !frame_skip_text_entry) { frame_skip_text_entry = false; - if(event.type == sf::Event::TextEntered) { + if(event.type == mgl::Event::TextEntered) { // TODO: Also show typing event when ctrl+v pasting? - if(event.text.unicode != 13) { // Return key + if(event.text.codepoint != 13) { // Return key start_typing_timer.restart(); if(!typing && current_room) { fprintf(stderr, "Started typing\n"); @@ -6412,7 +6367,7 @@ namespace QuickMedia { } typing = true; } - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Escape) { if(mention.visible) { mention.hide(); } else { @@ -6504,7 +6459,7 @@ namespace QuickMedia { // TODO: Instead of doing this, exit this current function and navigate to chat login page instead. //delete current_plugin; //current_plugin = new Matrix(); - window.setTitle("QuickMedia - matrix"); + window.set_title("QuickMedia - matrix"); current_page = PageType::CHAT_LOGIN; chat_login_page(); if(current_page == PageType::CHAT) @@ -6531,7 +6486,7 @@ namespace QuickMedia { break; } - if(typing && start_typing_timer.getElapsedTime().asSeconds() >= typing_timeout_seconds && current_room) { + if(typing && start_typing_timer.get_elapsed_time_seconds() >= typing_timeout_seconds && current_room) { fprintf(stderr, "Stopped typing\n"); typing = false; typing_state_queue.push(false); @@ -6540,10 +6495,9 @@ namespace QuickMedia { if(!current_room->body_item->thumbnail_url.empty()) room_avatar_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(current_room->body_item->thumbnail_url, false, AVATAR_THUMBNAIL_SIZE); - if(room_avatar_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && room_avatar_thumbnail_data->image->getSize().x > 0 && room_avatar_thumbnail_data->image->getSize().y > 0) { - if(!room_avatar_thumbnail_data->texture.loadFromImage(*room_avatar_thumbnail_data->image)) + if(room_avatar_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && room_avatar_thumbnail_data->image->get_size().x > 0 && room_avatar_thumbnail_data->image->get_size().y > 0) { + if(!room_avatar_thumbnail_data->texture.load_from_image(*room_avatar_thumbnail_data->image)) fprintf(stderr, "Warning: failed to load texture for room avatar\n"); - room_avatar_thumbnail_data->texture.setSmooth(true); //room_avatar_thumbnail_data->texture.generateMipmap(); room_avatar_thumbnail_data->image.reset(); room_avatar_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE; @@ -6552,13 +6506,13 @@ namespace QuickMedia { if(room_avatar_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && !avatar_applied) { avatar_applied = true; - room_avatar_sprite.setTexture(room_avatar_thumbnail_data->texture, true); - auto texture_size = room_avatar_sprite.getTexture()->getSize(); + room_avatar_sprite.set_texture(&room_avatar_thumbnail_data->texture); + auto texture_size = room_avatar_sprite.get_texture()->get_size(); if(texture_size.x > 0 && texture_size.y > 0) { float width_ratio = (float)texture_size.x / (float)texture_size.y; float height_scale = room_avatar_height / (float)texture_size.y; float width_scale = height_scale * width_ratio; - room_avatar_sprite.setScale(width_scale * get_config().scale, height_scale * get_config().scale); + room_avatar_sprite.set_scale(mgl::vec2f(width_scale * get_config().scale, height_scale * get_config().scale)); } redraw = true; } @@ -6590,28 +6544,28 @@ namespace QuickMedia { const float body_width = window_size.x; - this->body_pos = sf::Vector2f(0.0f, tab_shade_height); + this->body_pos = mgl::vec2f(0.0f, tab_shade_height); if(window_size.x > 900.0f * get_config().scale * get_config().font_scale && show_room_side_panel) { this->body_size = vec2f_floor(300.0f * get_config().scale * get_config().font_scale, window_size.y - tab_shade_height); draw_room_list = true; } else { - this->body_size = sf::Vector2f(0.0f, 0.0f); + this->body_size = mgl::vec2f(0.0f, 0.0f); draw_room_list = false; } - body_pos = sf::Vector2f(this->body_pos.x + this->body_size.x, tab_shade_height); - body_size = sf::Vector2f(body_width - this->body_pos.x - this->body_size.x, window_size.y - chat_input_height_full - tab_shade_height); + body_pos = mgl::vec2f(this->body_pos.x + this->body_size.x, tab_shade_height); + body_size = mgl::vec2f(body_width - this->body_pos.x - this->body_size.x, window_size.y - chat_input_height_full - tab_shade_height); - chat_input_shade.setSize(sf::Vector2f(window_size.x - body_pos.x, chat_input_height_full)); - chat_input_shade.setPosition(body_pos.x, window_size.y - chat_input_shade.getSize().y); + chat_input_shade.set_size(mgl::vec2f(window_size.x - body_pos.x, chat_input_height_full)); + chat_input_shade.set_position(mgl::vec2f(body_pos.x, window_size.y - chat_input_shade.get_size().y)); chat_input.set_max_width(window_size.x - (logo_padding_x + logo_size.x + chat_input_padding_x + logo_padding_x + body_pos.x)); chat_input.set_position(vec2f_floor(body_pos.x + logo_padding_x + logo_size.x + chat_input_padding_x, window_size.y - chat_height - chat_input_padding_y)); - more_messages_below_rect.setSize(sf::Vector2f(chat_input_shade.getSize().x, gradient_height)); - more_messages_below_rect.setPosition(chat_input_shade.getPosition().x, std::floor(window_size.y - chat_input_height_full - gradient_height)); + more_messages_below_rect.set_size(mgl::vec2f(chat_input_shade.get_size().x, gradient_height)); + more_messages_below_rect.set_position(mgl::vec2f(chat_input_shade.get_position().x, std::floor(window_size.y - chat_input_height_full - gradient_height))); - logo_sprite.setPosition(body_pos.x + logo_padding_x, std::floor(window_size.y - chat_input_height_full * 0.5f - logo_size.y * 0.5f)); + logo_sprite.set_position(mgl::vec2f(body_pos.x + logo_padding_x, std::floor(window_size.y - chat_input_height_full * 0.5f - logo_size.y * 0.5f))); } sync_data.messages.clear(); @@ -6716,52 +6670,52 @@ namespace QuickMedia { } else { tabs[selected_tab].body->draw(window, body_pos, body_size); if(selected_tab == MESSAGES_TAB_INDEX && mention.visible && chat_state == ChatState::TYPING_MESSAGE) { - sf::RectangleShape user_mention_background(sf::Vector2f(body_size.x, user_mention_body_height)); - user_mention_background.setPosition(sf::Vector2f(body_pos.x, body_pos.y + body_size.y - user_mention_body_height)); - user_mention_background.setFillColor(get_theme().shade_color); + mgl::Rectangle user_mention_background(mgl::vec2f(body_size.x, user_mention_body_height)); + user_mention_background.set_position(mgl::vec2f(body_pos.x, body_pos.y + body_size.y - user_mention_body_height)); + user_mention_background.set_color(get_theme().shade_color); window.draw(user_mention_background); - tabs[USERS_TAB_INDEX].body->draw(window, user_mention_background.getPosition(), user_mention_background.getSize()); + tabs[USERS_TAB_INDEX].body->draw(window, user_mention_background.get_position(), user_mention_background.get_size()); } } - //tab_shade.setSize(sf::Vector2f(window_size.x, tab_shade_height)); + //tab_shade.set_size(mgl::vec2f(window_size.x, tab_shade_height)); //window.draw(tab_shade); if(selected_tab == MESSAGES_TAB_INDEX || selected_tab == PINNED_TAB_INDEX || selected_tab == USERS_TAB_INDEX) { float room_name_text_offset_x = std::floor(10.0f * get_config().scale); - if(room_avatar_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && room_avatar_sprite.getTexture() && room_avatar_sprite.getTexture()->getNativeHandle() != 0) { - auto room_avatar_texture_size = room_avatar_sprite.getTexture()->getSize(); - room_avatar_texture_size.x *= room_avatar_sprite.getScale().x; - room_avatar_texture_size.y *= room_avatar_sprite.getScale().y; - room_avatar_sprite.setPosition(body_pos.x + std::floor(10.0f * get_config().scale), room_name_total_height * 0.5f - room_avatar_texture_size.y * 0.5f + 5.0f); - circle_mask_shader.setUniform("resolution", sf::Vector2f(room_avatar_texture_size.x, room_avatar_texture_size.y)); + if(room_avatar_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && room_avatar_sprite.get_texture() && room_avatar_sprite.get_texture()->is_valid()) { + auto room_avatar_texture_size = room_avatar_sprite.get_texture()->get_size(); + room_avatar_texture_size.x *= room_avatar_sprite.get_scale().x; + room_avatar_texture_size.y *= room_avatar_sprite.get_scale().y; + room_avatar_sprite.set_position(mgl::vec2f(body_pos.x + std::floor(10.0f * get_config().scale), room_name_total_height * 0.5f - room_avatar_texture_size.y * 0.5f + 5.0f)); + circle_mask_shader.set_uniform("resolution", mgl::vec2f(room_avatar_texture_size.x, room_avatar_texture_size.y)); window.draw(room_avatar_sprite, &circle_mask_shader); room_name_text_offset_x += room_avatar_texture_size.x + 10.0f; } - room_name_text.setPosition(body_pos.x + room_name_text_offset_x, room_name_text_padding_y); + room_name_text.set_position(mgl::vec2f(body_pos.x + room_name_text_offset_x, room_name_text_padding_y - 3.0f)); window.draw(room_name_text); - room_topic_text.setPosition(room_name_text.getPosition().x + room_name_text.getLocalBounds().width + 15.0f, room_name_text_padding_y + 2.0f + room_name_text_height * 0.5f - room_topic_text_height * 0.5f); + room_topic_text.set_position(mgl::vec2f(room_name_text.get_position().x + room_name_text.get_bounds().size.x + 15.0f, room_name_text_padding_y - 1.0f + room_name_text_height * 0.5f - room_topic_text_height * 0.5f)); window.draw(room_topic_text); } if(draw_room_list) { - sf::RectangleShape room_list_background(sf::Vector2f(this->body_size.x, window_size.y)); - //room_list_background.setPosition(this->body_pos); - room_list_background.setFillColor(get_theme().shade_color); + mgl::Rectangle room_list_background(mgl::vec2f(this->body_size.x, window_size.y)); + //room_list_background.set_position(this->body_pos); + room_list_background.set_color(get_theme().shade_color); window.draw(room_list_background); window.draw(room_label); const float tab_y = std::floor(tab_vertical_offset) + room_name_padding_y; - matrix_chat_page->rooms_page->body->draw(window, sf::Vector2f(0.0f, tab_y), sf::Vector2f(this->body_size.x, window_size.y - tab_y), Json::Value::nullSingleton()); + matrix_chat_page->rooms_page->body->draw(window, mgl::vec2f(0.0f, tab_y), mgl::vec2f(this->body_size.x, window_size.y - tab_y), Json::Value::nullSingleton()); } - ui_tabs.draw(window, sf::Vector2f(body_pos.x, std::floor(tab_vertical_offset) + room_name_padding_y), body_size.x); + ui_tabs.draw(window, mgl::vec2f(body_pos.x, std::floor(tab_vertical_offset) + room_name_padding_y), body_size.x); if(chat_state == ChatState::REPLYING || chat_state == ChatState::EDITING) { const float margin = 5.0f; - const float replying_to_text_height = replying_to_text.getLocalBounds().height + margin; + const float replying_to_text_height = replying_to_text.get_bounds().size.y + margin; float item_height = std::min(body_size.y - replying_to_text_height - margin, tabs[MESSAGES_TAB_INDEX].body->get_item_height(currently_operating_on_item.get(), body_size.x) + margin); if(item_height < 0.0f) @@ -6771,38 +6725,38 @@ namespace QuickMedia { if(mention.visible) extra_user_mention_height = user_mention_body_height; - sf::RectangleShape overlay(sf::Vector2f(window_size.x, window_size.y - chat_input_height_full - extra_user_mention_height)); - overlay.setFillColor(sf::Color(0, 0, 0, 240)); + mgl::Rectangle overlay(mgl::vec2f(window_size.x, window_size.y - chat_input_height_full - extra_user_mention_height)); + overlay.set_color(mgl::Color(0, 0, 0, 240)); window.draw(overlay); const float padding_x = std::floor(10.0f * get_config().scale); - sf::Vector2f body_item_pos(body_pos.x + padding_x, window_size.y - chat_input_height_full - item_height); - sf::Vector2f body_item_size(body_size.x - padding_x * 2.0f, item_height); + mgl::vec2f body_item_pos(body_pos.x + padding_x, window_size.y - chat_input_height_full - item_height); + mgl::vec2f body_item_size(body_size.x - padding_x * 2.0f, item_height); - sf::RectangleShape item_background(sf::Vector2f(window_size.x, body_item_size.y + chat_input_height_full + replying_to_text_height + margin)); - item_background.setPosition(sf::Vector2f(0.0f, window_size.y - (body_item_size.y + chat_input_height_full + replying_to_text_height + margin))); - item_background.setFillColor(get_theme().background_color); + mgl::Rectangle item_background(mgl::vec2f(window_size.x, body_item_size.y + chat_input_height_full + replying_to_text_height + margin)); + item_background.set_position(mgl::vec2f(0.0f, window_size.y - (body_item_size.y + chat_input_height_full + replying_to_text_height + margin))); + item_background.set_color(get_theme().background_color); window.draw(item_background); if(mention.visible) { - sf::RectangleShape user_mention_background(sf::Vector2f(window_size.x, user_mention_body_height)); - user_mention_background.setPosition(sf::Vector2f(0.0f, item_background.getPosition().y - user_mention_body_height)); - user_mention_background.setFillColor(get_theme().shade_color); + mgl::Rectangle user_mention_background(mgl::vec2f(window_size.x, user_mention_body_height)); + user_mention_background.set_position(mgl::vec2f(0.0f, item_background.get_position().y - user_mention_body_height)); + user_mention_background.set_color(get_theme().shade_color); window.draw(user_mention_background); tabs[USERS_TAB_INDEX].body->draw(window, - sf::Vector2f(body_pos.x + padding_x, item_background.getPosition().y - user_mention_body_height), - sf::Vector2f(body_size.x - padding_x * 2.0f, user_mention_body_height)); + mgl::vec2f(body_pos.x + padding_x, item_background.get_position().y - user_mention_body_height), + mgl::vec2f(body_size.x - padding_x * 2.0f, user_mention_body_height)); } - replying_to_text.setPosition(body_item_pos.x, body_item_pos.y - replying_to_text_height); + replying_to_text.set_position(mgl::vec2f(body_item_pos.x, body_item_pos.y - replying_to_text_height)); window.draw(replying_to_text); tabs[MESSAGES_TAB_INDEX].body->draw_item(window, currently_operating_on_item, body_item_pos, body_item_size); } if(selected_tab == MESSAGES_TAB_INDEX && current_room && current_room->body_item && (!current_room->last_message_read || has_unread_messages) && matrix->is_initial_sync_finished()) { - if(after_token.empty() && !tabs[selected_tab].body->is_bottom_cut_off() && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) { + if(after_token.empty() && !tabs[selected_tab].body->is_bottom_cut_off() && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.get_elapsed_time_seconds() >= read_marker_timeout_sec) { auto body_items = tabs[selected_tab].body->get_items(); int last_timeline_message = (int)body_items.size() - 1; for(int i = last_timeline_message - 1; i >= 0; --i) { @@ -6860,9 +6814,9 @@ namespace QuickMedia { // TODO: fetch_messages_dir double progress = 0.5 + std::sin(std::fmod(gradient_inc, 360.0) * 0.017453292519943295 - 1.5707963267948966*0.5) * 0.5; gradient_inc += (frame_time_ms * 0.5); - sf::Color top_color = interpolate_colors(get_theme().background_color, get_theme().loading_page_color, progress); + mgl::Color top_color = interpolate_colors(get_theme().background_color, get_theme().loading_page_color, progress); - gradient_points[0].position.x = chat_input_shade.getPosition().x; + gradient_points[0].position.x = chat_input_shade.get_position().x; gradient_points[0].position.y = tab_shade_height; gradient_points[1].position.x = window_size.x; @@ -6871,7 +6825,7 @@ namespace QuickMedia { gradient_points[2].position.x = window_size.x; gradient_points[2].position.y = tab_shade_height + gradient_height; - gradient_points[3].position.x = chat_input_shade.getPosition().x; + gradient_points[3].position.x = chat_input_shade.get_position().x; gradient_points[3].position.y = tab_shade_height + gradient_height; gradient_points[0].color = top_color; @@ -6880,10 +6834,10 @@ namespace QuickMedia { gradient_points[3].color = get_theme().background_color; if(fetch_messages_dir == MessageDirection::AFTER) { - gradient_points[0].position.y = more_messages_below_rect.getPosition().y; - gradient_points[1].position.y = more_messages_below_rect.getPosition().y; - gradient_points[2].position.y = more_messages_below_rect.getPosition().y + gradient_height; - gradient_points[3].position.y = more_messages_below_rect.getPosition().y + gradient_height; + gradient_points[0].position.y = more_messages_below_rect.get_position().y; + gradient_points[1].position.y = more_messages_below_rect.get_position().y; + gradient_points[2].position.y = more_messages_below_rect.get_position().y + gradient_height; + gradient_points[3].position.y = more_messages_below_rect.get_position().y + gradient_height; gradient_points[0].color = get_theme().background_color; gradient_points[1].color = get_theme().background_color; @@ -6891,7 +6845,7 @@ namespace QuickMedia { gradient_points[3].color = top_color; } - window.draw(gradient_points, 4, sf::Quads); // Note: sf::Quads doesn't work with egl + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); // Note: mgl::PrimitiveType::Quads doesn't work with egl } if(selected_tab == MESSAGES_TAB_INDEX) { @@ -6999,7 +6953,7 @@ namespace QuickMedia { matrix_chat_page->set_current_room(nullptr, nullptr); fetch_messages_future.cancel(); cleanup_tasks(); - window.setTitle("QuickMedia - matrix"); + window.set_title("QuickMedia - matrix"); return move_room; } @@ -7012,7 +6966,7 @@ namespace QuickMedia { } void Program::after_matrix_login_page() { - if(!window.isOpen()) + if(!window.is_open()) exit(exit_code); auto rooms_tags_body = create_body(); @@ -7078,7 +7032,7 @@ namespace QuickMedia { } void Program::download_page(std::string url) { - window.setTitle("QuickMedia - Select where you want to save " + std::string(url)); + window.set_title(("QuickMedia - Select where you want to save " + std::string(url)).c_str()); url = invidious_url_to_youtube_url(url); const bool download_use_youtube_dl = url_should_download_with_youtube_dl(url); @@ -7225,32 +7179,18 @@ namespace QuickMedia { string_replace_all(filename, '/', '_'); std::string output_filepath = file_save_page(filename); - if(!window.isOpen() || output_filepath.empty()) { + if(!window.is_open() || output_filepath.empty()) { exit_code = 1; return; } - sf::Vector2i monitor_size; - sf::Vector2i focused_monitor_center = get_focused_monitor_center(disp, monitor_size); + mgl::vec2i monitor_size; + mgl::vec2i focused_monitor_center = get_focused_monitor_center(disp, monitor_size); window_size.x = std::min(monitor_size.x, (int)(300.0f + 380.0f * get_config().scale)); window_size.y = std::min(monitor_size.y, (int)(50.0f + 130.0f * get_config().scale)); - window.setSize(sf::Vector2u(window_size.x, window_size.y)); - XSizeHints *size_hints = XAllocSizeHints(); - if(size_hints) { - size_hints->width = window_size.x; - size_hints->min_width = window_size.x; - size_hints->max_width = window_size.x; - - size_hints->height = window_size.y; - size_hints->min_height = window_size.y; - size_hints->max_height = window_size.y; - size_hints->flags = PSize | PMinSize | PMaxSize; - - XSetWMNormalHints(disp, x11_window, size_hints); - XFree(size_hints); - XFlush(disp); - } - window.setPosition(sf::Vector2i(focused_monitor_center.x - window_size.x * 0.5f, focused_monitor_center.y - window_size.y * 0.5f)); + window.set_size(mgl::vec2i(window_size.x, window_size.y)); + window.set_size_limits(window_size, window_size); + window.set_position(mgl::vec2i(focused_monitor_center.x - window_size.x * 0.5f, focused_monitor_center.y - window_size.y * 0.5f)); std::string output_filepath_s = output_filepath; char *output_dir = dirname(output_filepath_s.data()); @@ -7266,22 +7206,22 @@ namespace QuickMedia { const float loading_bar_padding_x = std::floor(4.0f * get_config().scale); const float loading_bar_padding_y = std::floor(4.0f * get_config().scale); - RoundedRectangle loading_bar_background(sf::Vector2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale), get_theme().background_color, &rounded_rectangle_shader); - RoundedRectangle loading_bar(sf::Vector2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale - loading_bar_padding_y), get_theme().loading_bar_color, &rounded_rectangle_shader); + RoundedRectangle loading_bar_background(mgl::vec2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale), get_theme().background_color, &rounded_rectangle_shader); + RoundedRectangle loading_bar(mgl::vec2f(1.0f, 1.0f), std::floor(10.0f * get_config().scale - loading_bar_padding_y), get_theme().loading_bar_color, &rounded_rectangle_shader); const float padding_x = std::floor(30.0f * get_config().scale); const float spacing_y = std::floor(15.0f * get_config().scale); const float loading_bar_height = std::floor(20.0f * get_config().scale); - sf::Text progress_text("0kb/Unknown", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(20.0f * get_config().scale * get_config().font_scale)); - sf::Text status_text("Downloading", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(20.0f * get_config().scale * get_config().font_scale)); - sf::Text filename_text(filename.c_str(), *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14.0f * get_config().scale * get_config().font_scale)); - filename_text.setFillColor(get_theme().faded_text_color); - sf::Text download_speed_text("0 bytes/s", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(14.0f * get_config().scale * get_config().font_scale)); - download_speed_text.setFillColor(get_theme().faded_text_color); + mgl::Text progress_text("0kb/Unknown", *FontLoader::get_font(FontLoader::FontType::LATIN, 20.0f * get_config().scale * get_config().font_scale)); + mgl::Text status_text("Downloading", *FontLoader::get_font(FontLoader::FontType::LATIN, 20.0f * get_config().scale * get_config().font_scale)); + mgl::Text filename_text(filename.c_str(), *FontLoader::get_font(FontLoader::FontType::LATIN, 14.0f * get_config().scale * get_config().font_scale)); + filename_text.set_color(get_theme().faded_text_color); + mgl::Text download_speed_text("0 bytes/s", *FontLoader::get_font(FontLoader::FontType::LATIN, 14.0f * get_config().scale * get_config().font_scale)); + download_speed_text.set_color(get_theme().faded_text_color); bool redraw = true; - sf::Event event; + mgl::Event event; std::unique_ptr<Downloader> downloader; if(download_use_youtube_dl) { @@ -7306,20 +7246,18 @@ namespace QuickMedia { return; } - sf::Clock frame_timer; - sf::Clock progress_update_timer; + mgl::Clock frame_timer; + mgl::Clock progress_update_timer; bool download_completed = false; float progress = 0.0f; float ui_progress = 0.0f; - while(window.isOpen()) { - while (window.pollEvent(event)) { + while(window.is_open()) { + while (window.poll_event(event)) { common_event_handler(event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); redraw = true; } } @@ -7332,7 +7270,7 @@ namespace QuickMedia { exit(exit_code); } - if(progress_update_timer.getElapsedTime().asSeconds() >= 1.0f) { + if(progress_update_timer.get_elapsed_time_seconds() >= 1.0f) { progress_update_timer.restart(); DownloadUpdateStatus update_status = downloader->update(); switch(update_status) { @@ -7348,29 +7286,29 @@ namespace QuickMedia { progress = downloader->get_progress(); progress = std::max(0.0f, std::min(1.0f, progress)); - progress_text.setString(downloader->get_progress_text()); - download_speed_text.setString(downloader->get_download_speed_text()); + progress_text.set_string(downloader->get_progress_text()); + download_speed_text.set_string(downloader->get_download_speed_text()); redraw = true; } if(redraw) { redraw = false; - loading_bar_background.set_size(sf::Vector2f(window_size.x - padding_x * 2.0f, loading_bar_height)); - loading_bar_background.set_position(window_size * 0.5f - loading_bar_background.get_size() * 0.5f + sf::Vector2f(0.0f, download_speed_text.getLocalBounds().height * 0.5f)); + loading_bar_background.set_size(mgl::vec2f(window_size.x - padding_x * 2.0f, loading_bar_height)); + loading_bar_background.set_position(window_size.to_vec2f() * 0.5f - loading_bar_background.get_size() * 0.5f + mgl::vec2f(0.0f, download_speed_text.get_bounds().size.y * 0.5f)); loading_bar_background.set_position(vec2f_floor(loading_bar_background.get_position().x, loading_bar_background.get_position().y)); - loading_bar.set_position(loading_bar_background.get_position() + sf::Vector2f(loading_bar_padding_x, loading_bar_padding_y)); - filename_text.setPosition( - loading_bar_background.get_position() + sf::Vector2f(0.0f, -(filename_text.getLocalBounds().height + spacing_y))); - progress_text.setPosition( - filename_text.getPosition() + sf::Vector2f(loading_bar_background.get_size().x - progress_text.getLocalBounds().width, -(progress_text.getLocalBounds().height + spacing_y))); - status_text.setPosition( - filename_text.getPosition() + sf::Vector2f(0.0f, -(status_text.getLocalBounds().height + spacing_y))); - download_speed_text.setPosition( - loading_bar_background.get_position() + sf::Vector2f(0.0f, loading_bar_height + spacing_y)); + loading_bar.set_position(loading_bar_background.get_position() + mgl::vec2f(loading_bar_padding_x, loading_bar_padding_y)); + filename_text.set_position( + loading_bar_background.get_position() + mgl::vec2f(0.0f, -(filename_text.get_bounds().size.y + spacing_y))); + progress_text.set_position( + filename_text.get_position() + mgl::vec2f(loading_bar_background.get_size().x - progress_text.get_bounds().size.x, -(progress_text.get_bounds().size.y + spacing_y))); + status_text.set_position( + filename_text.get_position() + mgl::vec2f(0.0f, -(status_text.get_bounds().size.y + spacing_y))); + download_speed_text.set_position( + loading_bar_background.get_position() + mgl::vec2f(0.0f, loading_bar_height + spacing_y)); } const float progress_diff = progress - ui_progress; - const float progress_move = frame_timer.getElapsedTime().asSeconds() * 500.0f * std::abs(progress_diff); + const float progress_move = frame_timer.get_elapsed_time_seconds() * 500.0f * std::abs(progress_diff); if(std::abs(progress_diff) < progress_move) { ui_progress = progress; } else { @@ -7380,7 +7318,7 @@ namespace QuickMedia { ui_progress -= progress_move; } - loading_bar.set_size(sf::Vector2f( + loading_bar.set_size(mgl::vec2f( std::floor((loading_bar_background.get_size().x - loading_bar_padding_x * 2.0f) * ui_progress), loading_bar_height - loading_bar_padding_y * 2.0f)); @@ -7409,10 +7347,10 @@ namespace QuickMedia { } std::string Program::file_save_page(const std::string &filename) { - sf::Vector2f body_pos; - sf::Vector2f body_size; + mgl::vec2f body_pos; + mgl::vec2f body_size; bool redraw = true; - sf::Event event; + mgl::Event event; auto file_manager_page = std::make_unique<FileManagerPage>(this); file_manager_page->set_current_directory(file_manager_start_dir); @@ -7431,8 +7369,8 @@ namespace QuickMedia { }; search_bar->onTextSubmitCallback = [this, &search_bar, &file_manager_body, &file_manager_page, &ui_tabs, tab_path_index](const std::string&) { - if(sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl)) - return; + if(window.is_key_pressed(mgl::Keyboard::LControl) || window.is_key_pressed(mgl::Keyboard::RControl)) + return; BodyItem *selected = file_manager_body->get_selected(); if(!selected) @@ -7459,28 +7397,27 @@ namespace QuickMedia { const float bottom_panel_padding = 10.0f; const float bottom_panel_spacing = 10.0f; - Button cancel_button("Cancel", FontLoader::get_font(FontLoader::FontType::LATIN), 16, 100.0f, &rounded_rectangle_shader, get_config().scale * get_config().font_scale); + Button cancel_button("Cancel", FontLoader::get_font(FontLoader::FontType::LATIN, 16 * get_config().scale), 100.0f, &rounded_rectangle_shader, get_config().scale * get_config().font_scale); cancel_button.set_background_color(get_theme().cancel_button_background_color); - Button save_button("Save", FontLoader::get_font(FontLoader::FontType::LATIN), 16, 100.0f, &rounded_rectangle_shader, get_config().scale * get_config().font_scale); + Button save_button("Save", FontLoader::get_font(FontLoader::FontType::LATIN, 16 * get_config().scale), 100.0f, &rounded_rectangle_shader, get_config().scale * get_config().font_scale); save_button.set_background_color(get_theme().confirm_button_background_color); - sf::Text file_name_label("File name:", *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(16.0f * get_config().scale * get_config().font_scale)); + mgl::Text file_name_label("File name:", *FontLoader::get_font(FontLoader::FontType::LATIN, 16.0f * get_config().scale * get_config().font_scale)); Entry file_name_entry("", &rounded_rectangle_shader); file_name_entry.set_text(filename); file_name_entry.set_single_line(true); file_name_entry.set_editable(false); - sf::RectangleShape bottom_panel_background; - bottom_panel_background.setFillColor(get_theme().shade_color); + mgl::Rectangle bottom_panel_background; + bottom_panel_background.set_color(get_theme().shade_color); const float gradient_height = 5.0f; - sf::Vertex gradient_points[4]; + mgl::Vertex gradient_points[4]; auto save_file = [this, &file_name_entry, &file_manager_page]() -> std::string { - auto u8 = file_name_entry.get_text().toUtf8(); - std::string *filename = (std::string*)&u8; + const std::string *filename = &file_name_entry.get_text(); Path filename_full_path = file_manager_page->get_current_directory().string(); filename_full_path.join(*filename); @@ -7520,15 +7457,15 @@ namespace QuickMedia { float prev_entry_height = file_name_entry.get_height(); - while (window.isOpen()) { - while (window.pollEvent(event)) { + while (window.is_open()) { + while (window.poll_event(event)) { common_event_handler(event); if(file_manager_body->on_event(window, event, !file_name_entry.is_editable())) idle_active_handler(); else event_idle_handler(event); - search_bar->on_event(event); + search_bar->on_event(window, event); if(cancel_button.on_event(event) & BUTTON_EVENT_CLICKED) return ""; if(save_button.on_event(event) & BUTTON_EVENT_CLICKED) { @@ -7536,25 +7473,23 @@ namespace QuickMedia { if(!save_path.empty()) return save_path; } - file_name_entry.process_event(event); + file_name_entry.process_event(window, event); - if(event.type == sf::Event::Resized) { + if(event.type == mgl::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; - sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); - window.setView(sf::View(visible_area)); redraw = true; idle_active_handler(); - } else if(event.type == sf::Event::GainedFocus) { + } else if(event.type == mgl::Event::GainedFocus) { redraw = true; - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Tab) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Tab) { file_name_entry.set_editable(!file_name_entry.is_editable()); search_bar->set_editable(!search_bar->is_editable()); - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Enter && event.key.control) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Enter && event.key.control) { std::string save_path = save_file(); if(!save_path.empty()) return save_path; - } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Escape) { window.close(); } } @@ -7573,30 +7508,30 @@ namespace QuickMedia { get_body_dimensions(window_size, search_bar.get(), body_pos, body_size); body_pos.y += Tabs::get_shade_height(); body_size.y -= Tabs::get_shade_height(); - save_button.set_position(window_size - sf::Vector2f(save_button.get_width(), save_button.get_height()) - sf::Vector2f(bottom_panel_padding, bottom_panel_padding)); - cancel_button.set_position(save_button.get_position() - sf::Vector2f(cancel_button.get_width() + bottom_panel_spacing, 0.0f)); - file_name_label.setPosition(sf::Vector2f(bottom_panel_spacing, std::floor(window_size.y - bottom_panel_padding - file_name_entry.get_height() * 0.5f - file_name_label.getLocalBounds().height * 0.5f - 5.0f * get_config().scale))); - file_name_entry.set_position(sf::Vector2f(file_name_label.getPosition().x + file_name_label.getLocalBounds().width + bottom_panel_spacing, window_size.y - file_name_entry.get_height() - bottom_panel_padding)); - file_name_entry.set_max_width(std::floor(cancel_button.get_position().x - bottom_panel_spacing - file_name_label.getLocalBounds().width - bottom_panel_spacing - bottom_panel_spacing)); - bottom_panel_background.setPosition(0.0f, window_size.y - std::floor(bottom_panel_padding * 2.0f + file_name_entry.get_height())); - bottom_panel_background.setSize(sf::Vector2f(window_size.x, std::floor(bottom_panel_padding * 2.0f + file_name_entry.get_height()))); + save_button.set_position(window_size.to_vec2f() - mgl::vec2f(save_button.get_width(), save_button.get_height()) - mgl::vec2f(bottom_panel_padding, bottom_panel_padding)); + cancel_button.set_position(save_button.get_position() - mgl::vec2f(cancel_button.get_width() + bottom_panel_spacing, 0.0f)); + file_name_label.set_position(mgl::vec2f(bottom_panel_spacing, std::floor(window_size.y - bottom_panel_padding - file_name_entry.get_height() * 0.5f - file_name_label.get_bounds().size.y * 0.5f - 5.0f * get_config().scale))); + file_name_entry.set_position(mgl::vec2f(file_name_label.get_position().x + file_name_label.get_bounds().size.x + bottom_panel_spacing, window_size.y - file_name_entry.get_height() - bottom_panel_padding)); + file_name_entry.set_max_width(std::floor(cancel_button.get_position().x - bottom_panel_spacing - file_name_label.get_bounds().size.x - bottom_panel_spacing - bottom_panel_spacing)); + bottom_panel_background.set_position(mgl::vec2f(0.0f, window_size.y - std::floor(bottom_panel_padding * 2.0f + file_name_entry.get_height()))); + bottom_panel_background.set_size(mgl::vec2f(window_size.x, std::floor(bottom_panel_padding * 2.0f + file_name_entry.get_height()))); - const sf::Color color(0, 0, 0, 50); - gradient_points[0] = sf::Vertex(bottom_panel_background.getPosition() + sf::Vector2f(0.0f, -gradient_height), sf::Color(color.r, color.g, color.b, 0)); - gradient_points[1] = sf::Vertex(bottom_panel_background.getPosition() + sf::Vector2f(bottom_panel_background.getSize().x, -gradient_height), sf::Color(color.r, color.g, color.b, 0)); - gradient_points[2] = sf::Vertex(bottom_panel_background.getPosition() + sf::Vector2f(bottom_panel_background.getSize().x, 0.0f), color); - gradient_points[3] = sf::Vertex(bottom_panel_background.getPosition() + sf::Vector2f(0.0f, 0.0f), color); + const mgl::Color color(0, 0, 0, 50); + gradient_points[0] = mgl::Vertex(bottom_panel_background.get_position() + mgl::vec2f(0.0f, -gradient_height), mgl::Color(color.r, color.g, color.b, 0)); + gradient_points[1] = mgl::Vertex(bottom_panel_background.get_position() + mgl::vec2f(bottom_panel_background.get_size().x, -gradient_height), mgl::Color(color.r, color.g, color.b, 0)); + gradient_points[2] = mgl::Vertex(bottom_panel_background.get_position() + mgl::vec2f(bottom_panel_background.get_size().x, 0.0f), color); + gradient_points[3] = mgl::Vertex(bottom_panel_background.get_position() + mgl::vec2f(0.0f, 0.0f), color); } window.clear(get_theme().background_color); - ui_tabs.draw(window, sf::Vector2f(0.0f, search_bar->getBottomWithoutShadow()), window_size.x); - search_bar->draw(window, window_size, true); + ui_tabs.draw(window, mgl::vec2f(0.0f, search_bar->getBottomWithoutShadow()), window_size.x); + search_bar->draw(window, window_size.to_vec2f(), true); - file_manager_body->draw(window, body_pos, body_size - sf::Vector2f(0.0f, bottom_panel_background.getSize().y)); + file_manager_body->draw(window, body_pos, body_size - mgl::vec2f(0.0f, bottom_panel_background.get_size().y)); window.draw(bottom_panel_background); - window.draw(gradient_points, 4, sf::Quads); + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); window.draw(file_name_label); cancel_button.draw(window); save_button.draw(window); diff --git a/src/ResourceLoader.cpp b/src/ResourceLoader.cpp index 2725898..89ae510 100644 --- a/src/ResourceLoader.cpp +++ b/src/ResourceLoader.cpp @@ -2,16 +2,19 @@ #include "../include/Program.hpp" #include "../include/Path.hpp" #include "../include/StringUtils.hpp" -#include <SFML/Graphics/Font.hpp> -#include <SFML/Graphics/Texture.hpp> +#include <mglpp/graphics/Font.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/system/MemoryMappedFile.hpp> #include <array> #include <unordered_map> #include <malloc.h> #include <assert.h> static std::string resource_root; -static std::array<std::unique_ptr<sf::Font>, 4> font_cache; -static std::unordered_map<std::string, std::unique_ptr<sf::Texture>> texture_cache; +static std::array<std::unique_ptr<mgl::MemoryMappedFile>, 4> font_file_cache; +// font_cache[(unsigned int)font_type][character_size] +static std::array<std::unordered_map<unsigned int, std::unique_ptr<mgl::Font>>, 4> font_cache; +static std::unordered_map<std::string, std::unique_ptr<mgl::Texture>> texture_cache; static bool use_system_fonts = false; namespace QuickMedia { @@ -37,10 +40,12 @@ namespace QuickMedia::FontLoader { return 0; } - sf::Font* get_font(FontType font_type) { - sf::Font *font = font_cache[(size_t)font_type].get(); - if(!font) { - auto new_font = std::make_unique<sf::Font>(); + mgl::Font* get_font(FontType font_type, unsigned int character_size) { + // Make mgl font size match sfml font size + character_size += 5; + + mgl::MemoryMappedFile *mapped_file = font_file_cache[(unsigned int)font_type].get(); + if(!mapped_file) { std::vector<std::string> noto_directories; std::string font_file_name; switch(font_type) { @@ -95,41 +100,54 @@ namespace QuickMedia::FontLoader { } } - bool successfully_loaded_font = false; + bool successfully_loaded_file = false; + auto new_memory_mapped_file = std::make_unique<mgl::MemoryMappedFile>(); for(const std::string ¬o_dir : noto_directories) { - if(new_font->loadFromFile(noto_dir + "/" + font_file_name)) { - successfully_loaded_font = true; + if(new_memory_mapped_file->load((noto_dir + "/" + font_file_name).c_str(), mgl::MemoryMappedFile::LoadOptions{true, false})) { + successfully_loaded_file = true; break; } } - if(!successfully_loaded_font) - fprintf(stderr, "Warning: Failed to load font: %s\n", font_file_name.c_str()); + if(!successfully_loaded_file) { + fprintf(stderr, "Warning: Failed to load font file: %s\n", font_file_name.c_str()); + abort(); + } + + mapped_file = new_memory_mapped_file.get(); + font_file_cache[(unsigned int)font_type] = std::move(new_memory_mapped_file); + } + + mgl::Font *font = font_cache[(unsigned int)font_type][character_size].get(); + if(!font) { + auto new_font = std::make_unique<mgl::Font>(); + if(!new_font->load_from_file(*mapped_file, character_size)) { + fprintf(stderr, "Warning: Failed to load font at character size %u\n", character_size); + abort(); + } font = new_font.get(); - font_cache[(size_t)font_type] = std::move(new_font); - malloc_trim(0); + font_cache[(unsigned int)font_type][character_size] = std::move(new_font); } return font; } } namespace QuickMedia::TextureLoader { - sf::Texture* get_texture(const char *filepath) { + mgl::Texture* get_texture(const char *filepath, bool pixel_coordinates) { assert(!resource_root.empty()); std::string str = filepath; auto it = texture_cache.find(str); if(it != texture_cache.end()) return it->second.get(); - auto new_texture = std::make_unique<sf::Texture>(); - sf::Texture *result = new_texture.get(); - if(!new_texture->loadFromFile(resource_root + str)) + auto new_texture = std::make_unique<mgl::Texture>(); + mgl::Texture *result = new_texture.get(); + if(!new_texture->load_from_file((resource_root + str).c_str(), mgl::Texture::LoadOptions{ false, pixel_coordinates })) fprintf(stderr, "Failed to load image: %s%s\n", resource_root.c_str(), filepath); - new_texture->setSmooth(true); texture_cache[str] = std::move(new_texture); malloc_trim(0); return result; } -}
\ No newline at end of file +} diff --git a/src/RoundedRectangle.cpp b/src/RoundedRectangle.cpp index fc56823..587560c 100644 --- a/src/RoundedRectangle.cpp +++ b/src/RoundedRectangle.cpp @@ -1,68 +1,71 @@ #include "../include/RoundedRectangle.hpp" -#include <SFML/Graphics/Shader.hpp> -#include <SFML/Graphics/RenderTarget.hpp> -#include <atomic> +#include <mglpp/graphics/Shader.hpp> +#include <mglpp/window/Window.hpp> #include <assert.h> namespace QuickMedia { static const float shadow_radius = 20.0f; // Has to match the shadow offset in rounded_rectangle.glsl - static std::atomic<float> cached_radius = 0.0f; - static std::atomic<float> cached_resolution_x = 0.0f; - static std::atomic<float> cached_resolution_y = 0.0f; + static float cached_radius = 0.0f; + static float cached_resolution_x = 0.0f; + static float cached_resolution_y = 0.0f; - RoundedRectangle::RoundedRectangle(sf::Vector2f size, float radius, sf::Color color, sf::Shader *rounded_rectangle_shader) : + static float abs(float value) { + return value >= 0.0f ? value : -value; + } + + RoundedRectangle::RoundedRectangle(mgl::vec2f size, float radius, mgl::Color color, mgl::Shader *rounded_rectangle_shader) : radius(radius), pos(0.0f, 0.0f), size(size), rounded_rectangle_shader(rounded_rectangle_shader) { assert(rounded_rectangle_shader); set_color(color); - vertices[0].texCoords = sf::Vector2f(0.0f, 0.0f); - vertices[1].texCoords = sf::Vector2f(1.0f, 0.0f); - vertices[2].texCoords = sf::Vector2f(1.0f, 1.0f); - vertices[3].texCoords = sf::Vector2f(0.0f, 1.0f); + vertices[0].texcoords = mgl::vec2f(0.0f, 0.0f); + vertices[1].texcoords = mgl::vec2f(1.0f, 0.0f); + vertices[2].texcoords = mgl::vec2f(1.0f, 1.0f); + vertices[3].texcoords = mgl::vec2f(0.0f, 1.0f); } - void RoundedRectangle::set_position(sf::Vector2f pos) { + void RoundedRectangle::set_position(mgl::vec2f pos) { this->pos = pos; - const float shadow_radius_ins = rounded_rectangle_shader->getNativeHandle() == 0 ? 0.0f : shadow_radius; - vertices[0].position = pos + sf::Vector2f(-shadow_radius_ins, -shadow_radius_ins); - vertices[1].position = pos + sf::Vector2f(shadow_radius_ins, -shadow_radius_ins) + sf::Vector2f(size.x, 0.0f); - vertices[2].position = pos + sf::Vector2f(shadow_radius_ins, shadow_radius_ins) + sf::Vector2f(size.x, size.y); - vertices[3].position = pos + sf::Vector2f(-shadow_radius_ins, shadow_radius_ins) + sf::Vector2f(0.0f, size.y); + const float shadow_radius_ins = rounded_rectangle_shader->is_valid() ? shadow_radius : 0.0f; + vertices[0].position = pos + mgl::vec2f(-shadow_radius_ins, -shadow_radius_ins); + vertices[1].position = pos + mgl::vec2f(shadow_radius_ins, -shadow_radius_ins) + mgl::vec2f(size.x, 0.0f); + vertices[2].position = pos + mgl::vec2f(shadow_radius_ins, shadow_radius_ins) + mgl::vec2f(size.x, size.y); + vertices[3].position = pos + mgl::vec2f(-shadow_radius_ins, shadow_radius_ins) + mgl::vec2f(0.0f, size.y); } - void RoundedRectangle::set_size(sf::Vector2f size) { + void RoundedRectangle::set_size(mgl::vec2f size) { this->size = size; set_position(pos); } - void RoundedRectangle::set_color(sf::Color color) { + void RoundedRectangle::set_color(mgl::Color color) { for(size_t i = 0; i < 4; ++i) vertices[i].color = color; } - sf::Vector2f RoundedRectangle::get_position() const { + mgl::vec2f RoundedRectangle::get_position() const { return pos; } - sf::Vector2f RoundedRectangle::get_size() const { + mgl::vec2f RoundedRectangle::get_size() const { return size; } - void RoundedRectangle::draw(sf::RenderTarget &target) { - const sf::Vector2f resolution = size + sf::Vector2f(shadow_radius*2.0f, shadow_radius*2.0f); + void RoundedRectangle::draw(mgl::Window &target) { + const mgl::vec2f resolution = size + mgl::vec2f(shadow_radius*2.0f, shadow_radius*2.0f); - // TODO: Remove these for optimizations. Also do the same in other places where setUniform is called - if(std::abs(cached_radius - radius) > 0.01f) { - rounded_rectangle_shader->setUniform("radius", radius); + if(abs(cached_radius - radius) > 0.01f) { + // TODO: Remove these for optimizations. Also do the same in other places where set_uniform is called + rounded_rectangle_shader->set_uniform("radius", radius); cached_radius = radius; } - if(std::abs(cached_resolution_x - resolution.x) > 0.01f || std::abs(cached_resolution_y - resolution.y) > 0.01f) { - rounded_rectangle_shader->setUniform("resolution", size + sf::Vector2f(shadow_radius*2.0f, shadow_radius*2.0f)); + if(abs(cached_resolution_x - resolution.x) > 0.01f || abs(cached_resolution_y - resolution.y) > 0.01f) { + rounded_rectangle_shader->set_uniform("resolution", size + mgl::vec2f(shadow_radius*2.0f, shadow_radius*2.0f)); cached_resolution_x = resolution.x; cached_resolution_y = resolution.y; } - target.draw(vertices, 4, sf::Quads, rounded_rectangle_shader); + target.draw(vertices, 4, mgl::PrimitiveType::Quads, rounded_rectangle_shader); } }
\ No newline at end of file diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp index 33cea08..e6c51e9 100644 --- a/src/SearchBar.cpp +++ b/src/SearchBar.cpp @@ -4,28 +4,30 @@ #include "../include/ResourceLoader.hpp" #include "../include/Config.hpp" #include "../include/Utils.hpp" -#include <SFML/Window/Event.hpp> -#include <SFML/Window/Clipboard.hpp> -#include <SFML/Graphics/RenderWindow.hpp> +#include <mglpp/system/FloatRect.hpp> +#include <mglpp/system/Utf8.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/window/Window.hpp> #include <cmath> #include <assert.h> -// TODO: Use a seperate placeholder sf::Text instead of switching the text to placeholder text.... +// TODO: Use a seperate placeholder mgl::Text instead of switching the text to placeholder text.... namespace QuickMedia { static const float background_margin_horizontal = 10.0f + std::floor(5.0f * get_config().scale); static const float padding_top_default = std::floor(10.0f * get_config().scale); static const float padding_bottom_default = std::floor(15.0f * get_config().scale); static const float background_margin_vertical = std::floor(4.0f * get_config().scale); + static const int character_size = get_config().search.font_size * get_config().scale * get_config().font_scale; - SearchBar::SearchBar(sf::Texture *plugin_logo, sf::Shader *rounded_rectangle_shader, const std::string &placeholder, bool input_masked) : + SearchBar::SearchBar(mgl::Texture *plugin_logo, mgl::Shader *rounded_rectangle_shader, const std::string &placeholder, bool input_masked) : onTextUpdateCallback(nullptr), onTextSubmitCallback(nullptr), onTextBeginTypingCallback(nullptr), - text_autosearch_delay(50), + text_autosearch_delay_ms(50), caret_visible(true), - text(placeholder, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(get_config().search.font_size * get_config().scale * get_config().font_scale)), - background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), + text(placeholder, *FontLoader::get_font(FontLoader::FontType::LATIN, character_size)), + background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), placeholder_str(placeholder), show_placeholder(true), updated_search(false), @@ -40,13 +42,13 @@ namespace QuickMedia { { padding_top = padding_top_default; padding_bottom = padding_bottom_default; - text.setFillColor(get_theme().placeholder_text_color); - shade.setFillColor(get_theme().shade_color); - if(plugin_logo && plugin_logo->getNativeHandle() != 0) - plugin_logo_sprite.setTexture(*plugin_logo, true); + text.set_color(get_theme().placeholder_text_color); + shade.set_color(get_theme().shade_color); + if(plugin_logo && plugin_logo->is_valid()) + plugin_logo_sprite.set_texture(plugin_logo); } - void SearchBar::draw(sf::RenderWindow &window, sf::Vector2f size, bool draw_background) { + void SearchBar::draw(mgl::Window &window, mgl::vec2f size, bool draw_background) { if(std::abs(size.x - prev_size.x) > 1.0f || std::abs(size.y - prev_size.y) > 1.0f) { needs_update = true; prev_size = size; @@ -63,17 +65,17 @@ namespace QuickMedia { background.draw(window); if(input_masked && !show_placeholder) { - std::string masked_str(text.getString().getSize(), '*'); - sf::Text masked_text(std::move(masked_str), *text.getFont(), text.getCharacterSize()); - masked_text.setPosition(text.getPosition()); + std::string masked_str(text.get_string().size(), '*'); + mgl::Text masked_text(std::move(masked_str), *text.get_font()); + masked_text.set_position(text.get_position()); window.draw(masked_text); - caret.setPosition(masked_text.findCharacterPos(masked_text.getString().getSize()) + sf::Vector2f(0.0f, 2.0f)); + caret.set_position(masked_text.find_character_pos(masked_text.get_string().size()) + mgl::vec2f(0.0f, character_size * 0.4f)); } else { window.draw(text); - if(show_placeholder || text.getString().isEmpty()) - caret.setPosition(text.getPosition() - sf::Vector2f(2.0f, 0.0f) + sf::Vector2f(0.0f, 2.0f)); + if(show_placeholder || text.get_string().empty()) + caret.set_position(text.get_position() - mgl::vec2f(2.0f, 0.0f) + mgl::vec2f(0.0f, character_size * 0.4f)); else - caret.setPosition(text.findCharacterPos(text.getString().getSize()) + sf::Vector2f(0.0f, 2.0f)); + caret.set_position(text.find_character_pos(text.get_string().size()) + mgl::vec2f(0.0f, character_size * 0.4f)); } if(caret_visible && is_editable()) @@ -83,16 +85,16 @@ namespace QuickMedia { window.draw(plugin_logo_sprite); } - void SearchBar::on_event(sf::Event &event) { - if(is_touch_enabled() && event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) { - sf::FloatRect box(background.get_position(), background.get_size()); - if(box.contains(event.mouseButton.x, event.mouseButton.y)) + void SearchBar::on_event(mgl::Window &window, mgl::Event &event) { + if(is_touch_enabled() && event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { + mgl::FloatRect box(background.get_position(), background.get_size()); + if(box.contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) mouse_left_inside = true; else mouse_left_inside = false; - } else if(is_touch_enabled() && event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left) { - sf::FloatRect box(background.get_position(), background.get_size()); - if(mouse_left_inside && box.contains(event.mouseButton.x, event.mouseButton.y)) + } else if(is_touch_enabled() && event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Left) { + mgl::FloatRect box(background.get_position(), background.get_size()); + if(mouse_left_inside && box.contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) show_virtual_keyboard(); mouse_left_inside = false; } @@ -100,63 +102,70 @@ namespace QuickMedia { if(!editable) return; - if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Backspace) + if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Backspace) backspace_pressed = true; - else if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Backspace) + else if(event.type == mgl::Event::KeyReleased && event.key.code == mgl::Keyboard::Backspace) backspace_pressed = false; - if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::V && event.key.control) { - auto clipboard = sf::Clipboard::getString().toUtf8(); - append_text(*(std::string*)&clipboard); + if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::V && event.key.control) { + append_text(window.get_clipboard()); } - if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::D && event.key.control) { - clear(); - updated_search = true; - time_since_search_update.restart(); - } - - if((sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl)) && (event.type != sf::Event::TextEntered || event.text.unicode != 13)) // Enter + if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::D && event.key.control) { + if(!text.get_string().empty()) { + clear(); + updated_search = true; + time_since_search_update.restart(); + } return; + } - if(event.type == sf::Event::TextEntered && event.text.unicode != 8 && event.text.unicode != 127) // 8 = backspace, 127 = del - onTextEntered(event.text.unicode); - else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Backspace) - onTextEntered(8); + if((window.is_key_pressed(mgl::Keyboard::LControl) || window.is_key_pressed(mgl::Keyboard::RControl)) && (event.type != mgl::Event::TextEntered || event.text.codepoint != 13)) // Enter + return; + + if(event.type == mgl::Event::TextEntered && event.text.codepoint != 8 && event.text.codepoint != 127) { // 8 = backspace, 127 = del + onTextEntered(event.text); + } else if(event.type == mgl::Event::KeyPressed && event.key.code == mgl::Keyboard::Backspace) { + mgl::Event::TextEvent text_event; + text_event.codepoint = 8; + text_event.size = 1; + text_event.str[0] = 8; + text_event.str[1] = '\0'; + onTextEntered(text_event); + } } void SearchBar::update() { - sf::Int32 elapsed_time = time_since_search_update.getElapsedTime().asMilliseconds(); - int timeout = text_autosearch_delay; + double elapsed_time_sec = time_since_search_update.get_elapsed_time_seconds(); + double timeout_sec = (double)text_autosearch_delay_ms * 0.001; if(backspace_pressed) - timeout = 750; - if(updated_search && elapsed_time >= timeout) { + timeout_sec = 0.75; + if(updated_search && elapsed_time_sec >= timeout_sec) { updated_search = false; - auto u8 = text.getString().toUtf8(); - std::string *u8_str = (std::string*)&u8; + std::string str = text.get_string(); if(show_placeholder) - u8_str->clear(); + str.clear(); if(onTextUpdateCallback) - onTextUpdateCallback(*u8_str); + onTextUpdateCallback(str); typing = false; } } - void SearchBar::onWindowResize(const sf::Vector2f &size) { - draw_logo = plugin_logo_sprite.getTexture() != nullptr; + void SearchBar::onWindowResize(const mgl::vec2f &size) { + draw_logo = plugin_logo_sprite.get_texture() != nullptr; if(size.x < 400.0f) draw_logo = false; - float font_height = text.getCharacterSize() + 7.0f; + float font_height = character_size + 7.0f; float rect_height = std::floor(font_height + background_margin_vertical * 2.0f); float offset_x; if(draw_logo) { - float one_line_height = std::floor(text.getCharacterSize() + 8.0f + background_margin_vertical * 2.0f); - auto texture_size = plugin_logo_sprite.getTexture()->getSize(); - sf::Vector2f texture_size_f(texture_size.x, texture_size.y); - sf::Vector2f new_size = wrap_to_size(texture_size_f, sf::Vector2f(200.0f, one_line_height)); - plugin_logo_sprite.setScale(get_ratio(texture_size_f, new_size)); - plugin_logo_sprite.setPosition(pos.x + padding_x, pos.y + padding_top + rect_height * 0.5f - plugin_logo_sprite.getTexture()->getSize().y * plugin_logo_sprite.getScale().y * 0.5f); + float one_line_height = std::floor(character_size + 8.0f + background_margin_vertical * 2.0f); + auto texture_size = plugin_logo_sprite.get_texture()->get_size(); + mgl::vec2f texture_size_f(texture_size.x, texture_size.y); + mgl::vec2f new_size = wrap_to_size(texture_size_f, mgl::vec2f(200.0f, one_line_height)); + plugin_logo_sprite.set_scale(get_ratio(texture_size_f, new_size)); + plugin_logo_sprite.set_position(mgl::vec2f(pos.x + padding_x, pos.y + padding_top + rect_height * 0.5f - plugin_logo_sprite.get_texture()->get_size().y * plugin_logo_sprite.get_scale().y * 0.5f)); offset_x = padding_x + new_size.x + 10.0f; } else { offset_x = padding_x; @@ -164,29 +173,32 @@ namespace QuickMedia { const float width = std::floor(size.x - offset_x - padding_x); - background.set_size(sf::Vector2f(width, rect_height)); - shade.setSize(sf::Vector2f(size.x, padding_top + rect_height + padding_bottom)); - caret.setSize(vec2f_floor(2.0f * get_config().scale, text.getCharacterSize() + std::floor(2.0f * get_config().scale))); + background.set_size(mgl::vec2f(width, rect_height)); + shade.set_size(mgl::vec2f(size.x, padding_top + rect_height + padding_bottom)); + caret.set_size(vec2f_floor(2.0f * get_config().scale, character_size + std::floor(2.0f * get_config().scale))); - background.set_position(sf::Vector2f(pos.x + offset_x, pos.y + padding_top)); - shade.setPosition(pos); - sf::Vector2f font_position(std::floor(pos.x + offset_x + background_margin_horizontal), std::floor(pos.y + padding_top + background_margin_vertical)); - text.setPosition(font_position); + background.set_position(mgl::vec2f(pos.x + offset_x, pos.y + padding_top)); + shade.set_position(pos); + mgl::vec2f font_position(std::floor(pos.x + offset_x + background_margin_horizontal), std::floor(pos.y + padding_top + background_margin_vertical - character_size * 0.3f)); + text.set_position(font_position); } - void SearchBar::onTextEntered(sf::Uint32 codepoint) { - if(codepoint == 8 && !show_placeholder) { // Backspace - sf::String str = text.getString(); - if(str.getSize() > 0) { + void SearchBar::onTextEntered(const mgl::Event::TextEvent &text_event) { + if(text_event.codepoint == 8 && !show_placeholder) { // Backspace + std::string str = text.get_string(); + if(str.size() > 0) { // TODO: When it's possible to move the cursor, then check at cursor position instead of end of the string - if(str[str.getSize() - 1] == '\n') + if(str[str.size() - 1] == '\n') needs_update = true; - str.erase(str.getSize() - 1, 1); - text.setString(str); - if(str.getSize() == 0) { + + const size_t codepoint_start_index = mgl::utf8_get_start_of_codepoint((const unsigned char*)str.c_str(), str.size(), str.size() - 1); + str.erase(codepoint_start_index); + const bool empty = str.empty(); + text.set_string(std::move(str)); + if(empty) { show_placeholder = true; - text.setString(placeholder_str); - text.setFillColor(get_theme().placeholder_text_color); + text.set_string(placeholder_str); + text.set_color(get_theme().placeholder_text_color); } if(!updated_search) { typing = true; @@ -196,18 +208,17 @@ namespace QuickMedia { updated_search = true; time_since_search_update.restart(); } - } else if(codepoint == 13) { // Return + } else if(text_event.codepoint == 13) { // Return backspace_pressed = false; if(onTextSubmitCallback) { - auto u8 = text.getString().toUtf8(); - std::string *u8_str = (std::string*)&u8; + std::string str = text.get_string(); if(show_placeholder) - u8_str->clear(); - onTextSubmitCallback(*u8_str); + str.clear(); + onTextSubmitCallback(str); } - } else if(codepoint > 31) { // Non-control character - append_text(sf::String(codepoint)); - } else if(codepoint == '\n') + } else if(text_event.codepoint > 31) { // Non-control character + append_text(std::string(text_event.str, text_event.size)); + } else if(text_event.codepoint == '\n') needs_update = true; } @@ -215,8 +226,8 @@ namespace QuickMedia { if(show_placeholder) return; show_placeholder = true; - text.setString(placeholder_str); - text.setFillColor(get_theme().placeholder_text_color); + text.set_string(placeholder_str); + text.set_color(get_theme().placeholder_text_color); needs_update = true; updated_search = false; backspace_pressed = false; @@ -238,13 +249,11 @@ namespace QuickMedia { if(show_placeholder) { show_placeholder = false; - text.setString(""); - text.setFillColor(sf::Color::White); + text.set_string(""); + text.set_color(mgl::Color(255, 255, 255, 255)); } - sf::String str = text.getString(); - str += sf::String::fromUtf8(text_to_add.begin(), text_to_add.end());; - text.setString(str); + text.append_string(text_to_add); if(!updated_search) { typing = true; @@ -258,7 +267,7 @@ namespace QuickMedia { needs_update = true; } - void SearchBar::set_position(sf::Vector2f pos) { + void SearchBar::set_position(mgl::vec2f pos) { if(std::abs(this->pos.x - pos.x) > 1.0f || std::abs(this->pos.y - pos.y) > 1.0f) { this->pos = pos; needs_update = true; @@ -274,18 +283,18 @@ namespace QuickMedia { } float SearchBar::getBottom() const { - return getBottomWithoutShadow() + 5.0f;//background_shadow.getSize().y; + return getBottomWithoutShadow() + 5.0f;//background_shadow.get_size().y; } float SearchBar::getBottomWithoutShadow() const { - float font_height = text.getCharacterSize() + 7.0f; + float font_height = character_size + 7.0f; return std::floor(font_height + background_margin_vertical * 2.0f + padding_top + padding_bottom); } std::string SearchBar::get_text() const { if(show_placeholder) return ""; - return text.getString(); + return text.get_string(); } bool SearchBar::is_empty() const { diff --git a/src/SfmlFixes.cpp b/src/SfmlFixes.cpp deleted file mode 100644 index d6d4b17..0000000 --- a/src/SfmlFixes.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "../include/SfmlFixes.hpp" -#include <mutex> - -static std::mutex mutex; -namespace QuickMedia { - bool load_image_from_file(sf::Image &image, const std::string &filepath) { - std::lock_guard<std::mutex> lock(mutex); - return image.loadFromFile(filepath); - } - - bool load_image_from_memory(sf::Image &image, const void *data, size_t size) { - std::lock_guard<std::mutex> lock(mutex); - return image.loadFromMemory(data, size); - } -}
\ No newline at end of file diff --git a/src/Tabs.cpp b/src/Tabs.cpp index 4605f2c..db25ffb 100644 --- a/src/Tabs.cpp +++ b/src/Tabs.cpp @@ -4,8 +4,10 @@ #include "../include/Utils.hpp" #include "../include/Config.hpp" #include "../include/Theme.hpp" -#include <SFML/Window/Event.hpp> -#include <SFML/Graphics/RenderWindow.hpp> +#include "../include/Scale.hpp" +#include <mglpp/window/Event.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Texture.hpp> #include <cmath> namespace QuickMedia { @@ -24,15 +26,16 @@ namespace QuickMedia { return tab_height + std::floor(10.0f * get_config().scale); } - Tabs::Tabs(sf::Shader *rounded_rectangle_shader, sf::Color shade_color) : background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), shade_color(shade_color) { - shade.setFillColor(shade_color); + Tabs::Tabs(mgl::Shader *rounded_rectangle_shader, mgl::Color shade_color) : background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), shade_color(shade_color) { + shade.set_color(shade_color); + arrow_sprite.set_texture(TextureLoader::get_texture("images/arrow.png")); } - Tabs::Tabs(sf::Shader *rounded_rectangle_shader) : Tabs(rounded_rectangle_shader, get_theme().shade_color) {} + Tabs::Tabs(mgl::Shader *rounded_rectangle_shader) : Tabs(rounded_rectangle_shader, get_theme().shade_color) {} - int Tabs::add_tab(const std::string &title, Body *body) { + int Tabs::add_tab(std::string title, Body *body) { assert(body); - tabs.push_back({ sf::Text(title, *FontLoader::get_font(FontLoader::FontType::LATIN), tab_text_size), title, body} ); + tabs.push_back({ mgl::Text(std::move(title), *FontLoader::get_font(FontLoader::FontType::LATIN, tab_text_size)), body} ); return tabs.size() - 1; } @@ -62,13 +65,13 @@ namespace QuickMedia { on_change_tab(prev_tab, selected_tab); } - void Tabs::on_event(sf::Event &event) { - if(event.type == sf::Event::KeyPressed && event.key.control && !tabs.empty()) { - bool move_left = (event.key.code == sf::Keyboard::Left || (event.key.alt && event.key.code == sf::Keyboard::H)); - move_left |= (event.key.code == sf::Keyboard::Tab && event.key.shift); + void Tabs::on_event(mgl::Event &event) { + if(event.type == mgl::Event::KeyPressed && event.key.control && !tabs.empty()) { + bool move_left = (event.key.code == mgl::Keyboard::Left || (event.key.alt && event.key.code == mgl::Keyboard::H)); + move_left |= (event.key.code == mgl::Keyboard::Tab && event.key.shift); - bool move_right = (event.key.code == sf::Keyboard::Right || (event.key.alt && event.key.code == sf::Keyboard::L)); - move_right |= (event.key.code == sf::Keyboard::Tab && !event.key.shift); + bool move_right = (event.key.code == mgl::Keyboard::Right || (event.key.alt && event.key.code == mgl::Keyboard::L)); + move_right |= (event.key.code == mgl::Keyboard::Tab && !event.key.shift); if(move_left) { if(selected_tab > 0) @@ -76,37 +79,33 @@ namespace QuickMedia { } else if(move_right) { if(selected_tab < (int)tabs.size() - 1) move_selected_tab(selected_tab + 1); - } else if(event.key.code >= sf::Keyboard::Num1 && event.key.code <= sf::Keyboard::Num9) { - const int tab_target = event.key.code - sf::Keyboard::Num1; + } else if(event.key.code >= mgl::Keyboard::Num1 && event.key.code <= mgl::Keyboard::Num9) { + const int tab_target = event.key.code - mgl::Keyboard::Num1; if(tab_target < (int)tabs.size()) move_selected_tab(tab_target); } } } - static sf::View create_scissor_view(sf::Vector2f pos, sf::Vector2f size, const sf::Vector2f window_size) { - sf::View view(sf::FloatRect(0.0f, 0.0f, size.x, size.y)); - view.setViewport(sf::FloatRect( - pos.x / (float)window_size.x, pos.y / (float)window_size.y, - size.x / (float)window_size.x, size.y / (float)window_size.y)); + static mgl::View create_scissor_view(mgl::vec2f pos, mgl::vec2f size) { + mgl::View view = { mgl::vec2i(pos.x, pos.y), mgl::vec2i(size.x, size.y) }; return view; } - void Tabs::draw(sf::RenderWindow &window, sf::Vector2f pos, float width) { + void Tabs::draw(mgl::Window &window, mgl::vec2f pos, float width) { if(width - tab_margin_x < 0.0f || tabs.empty()) return; - auto window_size = window.getSize(); container_width = width; const int num_visible_tabs = std::min((int)tabs.size(), std::max(1, (int)(width / tab_min_width))); width_per_tab = std::floor(width / num_visible_tabs); - const float tab_text_y = std::floor(pos.y + tab_height*0.5f - (tab_text_size + 5.0f*get_config().scale)*0.5f); + const float tab_text_y = std::floor(pos.y + tab_height*0.5f - (tab_text_size + 13.0f*get_config().scale)*0.5f); tab_background_width = std::floor(width_per_tab - tab_margin_x*2.0f); - background.set_size(sf::Vector2f(tab_background_width, tab_height)); + background.set_size(mgl::vec2f(tab_background_width, tab_height)); - if(shade_color != sf::Color::Transparent) { - shade.setSize(sf::Vector2f(width, get_shade_height())); - shade.setPosition(std::floor(pos.x), std::floor(pos.y)); + if(shade_color != mgl::Color(0, 0, 0, 0)) { + shade.set_size(mgl::vec2f(width, get_shade_height())); + shade.set_position(mgl::vec2f(std::floor(pos.x), std::floor(pos.y))); window.draw(shade); } @@ -127,7 +126,7 @@ namespace QuickMedia { bool tabs_cutoff_right = false; const auto start_pos = pos; - const sf::View prev_view = window.getView(); + const mgl::View prev_view = window.get_view(); pos.x += scroll_fixed; for(size_t i = 0; i < tabs.size(); ++i) { @@ -142,83 +141,77 @@ namespace QuickMedia { } if((int)index == selected_tab) { - background.set_position(sf::Vector2f(background_pos_x, std::floor(pos.y))); + background.set_position(mgl::vec2f(background_pos_x, std::floor(pos.y))); background.draw(window); } - sf::Text &tab_text = tabs[index].text; - float text_pos_x = std::floor(pos.x + i*width_per_tab + width_per_tab*0.5f - tab_text.getLocalBounds().width*0.5f); + mgl::Text &tab_text = tabs[index].text; + float text_pos_x = std::floor(pos.x + i*width_per_tab + width_per_tab*0.5f - tab_text.get_bounds().size.x*0.5f); text_pos_x = std::max(text_pos_x, background_pos_x); - window.setView(create_scissor_view({ text_pos_x, tab_text_y }, { tab_background_width, tab_height }, { (float)window_size.x, (float)window_size.y })); + window.set_view(create_scissor_view({ text_pos_x, tab_text_y }, { tab_background_width, tab_height })); window.draw(tab_text); - window.setView(prev_view); + window.set_view(prev_view); } - float lw = std::floor(25.0f * get_config().scale); - float lh = background.get_size().y; - - float line_offset_y = std::floor(lw * 0.35f); + const float lw = std::floor(25.0f * get_config().scale); + const float lh = background.get_size().y; if(tabs_cutoff_left) { - sf::Vertex gradient_points[4]; - gradient_points[0].position = sf::Vector2f(start_pos.x + tab_margin_x, start_pos.y); - gradient_points[1].position = sf::Vector2f(start_pos.x + tab_margin_x + lw, start_pos.y); - gradient_points[2].position = sf::Vector2f(start_pos.x + tab_margin_x + lw, start_pos.y + lh); - gradient_points[3].position = sf::Vector2f(start_pos.x + tab_margin_x, start_pos.y + lh); + mgl::Vertex gradient_points[4]; + gradient_points[0].position = mgl::vec2f(start_pos.x + tab_margin_x, start_pos.y); + gradient_points[1].position = mgl::vec2f(start_pos.x + tab_margin_x + lw, start_pos.y); + gradient_points[2].position = mgl::vec2f(start_pos.x + tab_margin_x + lw, start_pos.y + lh); + gradient_points[3].position = mgl::vec2f(start_pos.x + tab_margin_x, start_pos.y + lh); gradient_points[0].color = shade_color; - gradient_points[1].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10); - gradient_points[2].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10); + gradient_points[1].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); + gradient_points[2].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); gradient_points[3].color = shade_color; - window.draw(gradient_points, 4, sf::Quads); - - sf::RectangleShape line(vec2f_floor(10.0f * get_config().scale, 2.0f * get_config().scale)); - line.setFillColor(get_theme().arrow_color); - line.setOrigin(line.getSize().x * 0.5f, line.getSize().y * 0.5f); - - line.rotate(-45.0f); - line.setPosition(std::floor(start_pos.x + line.getLocalBounds().width), std::floor(pos.y + background.get_size().y * 0.5f - lh * 0.5f + line_offset_y)); - window.draw(line); - - line.rotate(-90.0f); - line.setPosition(std::floor(start_pos.x + line.getLocalBounds().width), std::floor(pos.y + background.get_size().y * 0.5f - lh * 0.5f + line_offset_y + std::floor(7.0f * get_config().scale))); - window.draw(line); + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); + + mgl::vec2i arrow_sprite_width = arrow_sprite.get_texture()->get_size(); + arrow_sprite_width.y = arrow_sprite_width.x; + const mgl::vec2f arrow_scale_ratio = get_ratio(arrow_sprite_width, clamp_to_size_y(arrow_sprite_width, (int)lh - 8)); + arrow_sprite.set_scale(arrow_scale_ratio); + arrow_sprite.set_origin(arrow_sprite.get_texture()->get_size().to_vec2f() * 0.5f); + arrow_sprite.set_position(start_pos + mgl::vec2f((int)(arrow_sprite.get_texture()->get_size().x * 0.5f * arrow_scale_ratio.x) + 5, (int)(lh * 0.5f))); + arrow_sprite.set_rotation(-90.0f); + arrow_sprite.set_color(get_theme().arrow_color); + window.draw(arrow_sprite); } if(tabs_cutoff_right) { - sf::Vertex gradient_points[4]; - gradient_points[0].position = sf::Vector2f(start_pos.x + width - lw - tab_margin_x, start_pos.y); - gradient_points[1].position = sf::Vector2f(start_pos.x + width, start_pos.y); - gradient_points[2].position = sf::Vector2f(start_pos.x + width, start_pos.y + lh); - gradient_points[3].position = sf::Vector2f(start_pos.x + width - lw - tab_margin_x, start_pos.y + lh); + mgl::Vertex gradient_points[4]; + gradient_points[0].position = mgl::vec2f(start_pos.x + width - lw - tab_margin_x, start_pos.y); + gradient_points[1].position = mgl::vec2f(start_pos.x + width, start_pos.y); + gradient_points[2].position = mgl::vec2f(start_pos.x + width, start_pos.y + lh); + gradient_points[3].position = mgl::vec2f(start_pos.x + width - lw - tab_margin_x, start_pos.y + lh); - gradient_points[0].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10); + gradient_points[0].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); gradient_points[1].color = shade_color; gradient_points[2].color = shade_color; - gradient_points[3].color = sf::Color(shade_color.r, shade_color.g, shade_color.b, 10); - - window.draw(gradient_points, 4, sf::Quads); - - sf::RectangleShape line(vec2f_floor(10.0f * get_config().scale, 2.0f * get_config().scale)); - line.setFillColor(get_theme().arrow_color); - line.setOrigin(line.getSize().x * 0.5f, line.getSize().y * 0.5f); - - line.rotate(45.0f); - line.setPosition(std::floor(start_pos.x + width - lw*0.75f + line.getLocalBounds().width), std::floor(pos.y + background.get_size().y * 0.5f - lh * 0.5f + line_offset_y)); - window.draw(line); - - line.rotate(-90.0f); - line.setPosition(std::floor(start_pos.x + width - lw*0.75f + line.getLocalBounds().width), std::floor(pos.y + background.get_size().y * 0.5f - lh * 0.5f + line_offset_y + std::floor(7.0f * get_config().scale))); - window.draw(line); + gradient_points[3].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); + + window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); + + mgl::vec2i arrow_sprite_width = arrow_sprite.get_texture()->get_size(); + arrow_sprite_width.y = arrow_sprite_width.x; + const mgl::vec2f arrow_scale_ratio = get_ratio(arrow_sprite_width, clamp_to_size_y(arrow_sprite_width, (int)lh - 8)); + arrow_sprite.set_scale(arrow_scale_ratio); + arrow_sprite.set_origin(arrow_sprite.get_texture()->get_size().to_vec2f() * 0.5f); + arrow_sprite.set_position(start_pos + mgl::vec2f(width - (int)(arrow_sprite.get_texture()->get_size().x * 0.5f * arrow_scale_ratio.x) - 5, (int)(lh * 0.5f))); + arrow_sprite.set_rotation(90.0f); + arrow_sprite.set_color(get_theme().arrow_color); + window.draw(arrow_sprite); } } void Tabs::set_text(int index, const std::string &text) { - if(index < 0 || index >= (int)tabs.size() || text == tabs[index].label_utf8) return; - tabs[index].text.setString(sf::String::fromUtf8(text.begin(), text.end())); - tabs[index].label_utf8 = text; + if(index < 0 || index >= (int)tabs.size() || text == tabs[index].text.get_string()) + return; + tabs[index].text.set_string(text); } void Tabs::set_selected(int index) { diff --git a/src/Text.cpp b/src/Text.cpp index 14aebb1..69d6664 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -3,13 +3,13 @@ #include "../include/Config.hpp" #include "../include/Theme.hpp" #include "../generated/Emoji.hpp" -#include <SFML/Graphics/RectangleShape.hpp> -#include <SFML/Window/Clipboard.hpp> -#include <SFML/Window/Event.hpp> -#include <SFML/Graphics/RenderTarget.hpp> -#include <SFML/Graphics/Font.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/window/Event.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Font.hpp> +#include <mglpp/graphics/Texture.hpp> +#include <mglpp/system/Utf8.hpp> #include <cmath> -#include <functional> namespace QuickMedia { @@ -21,19 +21,8 @@ namespace QuickMedia static const size_t FONT_INDEX_SYMBOLS = 2; static const size_t FONT_INDEX_EMOJI = 3; static const size_t FONT_ARRAY_SIZE = 4; - - size_t StringViewUtf32::find(const StringViewUtf32 &other, size_t offset) const { - if(offset >= size) - return -1; - - auto it = std::search(data + offset, data + size - offset, std::boyer_moore_searcher(other.data, other.data + other.size)); - if(it != data + size) - return it - data; - - return -1; - } - Text::Text(sf::String _str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls) : + Text::Text(std::string _str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls) : bold_font(bold_font), characterSize(characterSize), maxWidth(maxWidth), @@ -50,52 +39,54 @@ namespace QuickMedia caretIndex(0), caret_offset_x(0.0f) { - for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) { - vertices[i].setPrimitiveType(sf::PrimitiveType::Triangles); - vertex_buffers[i] = sf::VertexBuffer(sf::PrimitiveType::Triangles, sf::VertexBuffer::Static); - } setString(std::move(_str)); } - void Text::setString(const sf::String &str) + void Text::setString(std::string str) { //if(str != this->str) //{ - size_t prev_str_size = this->str.getSize(); - this->str = str; + size_t prev_str_size = this->str.size(); + this->str = std::move(str); dirty = true; dirtyText = true; - if((int)this->str.getSize() < caretIndex || prev_str_size == 0) + if((int)this->str.size() < caretIndex || prev_str_size == 0) { - caretIndex = this->str.getSize(); + caretIndex = this->str.size(); dirtyCaret = true; } // } } - const sf::String& Text::getString() const + const std::string& Text::getString() const { return str; } - void Text::appendText(const sf::String &str) { + void Text::appendText(const std::string &str) { this->str += str; dirty = true; dirtyText = true; } + + void Text::insert_text_at_caret_position(const std::string &str) { + this->str.insert(caretIndex, str); + dirty = true; + dirtyText = true; + } - void Text::setPosition(float x, float y) + void Text::set_position(float x, float y) { position.x = x; position.y = y; } - void Text::setPosition(const sf::Vector2f &position) + void Text::set_position(const mgl::vec2f &position) { this->position = position; } - sf::Vector2f Text::getPosition() const + mgl::vec2f Text::get_position() const { return position; } @@ -106,7 +97,7 @@ namespace QuickMedia if(std::abs(maxWidth - this->maxWidth) > 1.0f) { this->maxWidth = maxWidth; - if(num_lines > 1 || maxWidth < boundingBox.width) { + if(num_lines > 1 || maxWidth < boundingBox.size.x) { dirty = true; dirtyCaret = true; } @@ -127,13 +118,13 @@ namespace QuickMedia } } - unsigned int Text::getCharacterSize() const + unsigned int Text::get_character_size() const { return characterSize; } - void Text::replace(size_t start_index, size_t length, const sf::String &insert_str) { - int string_diff = (int)insert_str.getSize() - (int)length; + void Text::replace(size_t start_index, size_t length, const std::string &insert_str) { + int string_diff = (int)insert_str.size() - (int)length; str.replace(start_index, length, insert_str); dirty = true; dirtyText = true; @@ -147,7 +138,7 @@ namespace QuickMedia return caretIndex; } - void Text::setFillColor(sf::Color color) + void Text::set_color(mgl::Color color) { if(color != this->color) { @@ -197,16 +188,16 @@ namespace QuickMedia float Text::getWidth() const { - return boundingBox.width; + return boundingBox.size.x; } float Text::getHeight() const { - return boundingBox.height; + return boundingBox.size.y; } // TODO: Is there a more efficient way to do this? maybe japanese characters have a specific bit-pattern? - static bool is_japanese_codepoint(sf::Uint32 codepoint) { + static bool is_japanese_codepoint(uint32_t codepoint) { return (codepoint >= 0x2E80 && codepoint <= 0x2FD5) // Kanji radicals || (codepoint >= 0x3000 && codepoint <= 0x303F) // Punctuation || (codepoint >= 0x3041 && codepoint <= 0x3096) // Hiragana @@ -221,12 +212,12 @@ namespace QuickMedia || (codepoint >= 0xFF5F && codepoint <= 0xFF9F); // Katakana and punctuation (half width) } - static bool is_korean_codepoint(sf::Uint32 codepoint) { + static bool is_korean_codepoint(uint32_t codepoint) { return codepoint >= 0xAC00 && codepoint <= 0xD7A3; } // TODO: Is there a more efficient way to do this? maybe chinese characters have a specific bit-pattern? - static bool is_chinese_codepoint(sf::Uint32 codepoint) { + static bool is_chinese_codepoint(uint32_t codepoint) { return (codepoint >= 0x4E00 && codepoint <= 0x9FFF) // CJK Unified Ideographs || (codepoint >= 0x3400 && codepoint <= 0x4DBF) // CJK Unified Ideographs Extension A || (codepoint >= 0x20000 && codepoint <= 0x2A6DF) // CJK Unified Ideographs Extension B @@ -238,44 +229,84 @@ namespace QuickMedia } // TODO: Merge chinese, japanese and korean codepoints into one function since they share ranges - static bool is_cjk_codepoint(sf::Uint32 codepoint) { + static bool is_cjk_codepoint(uint32_t codepoint) { return is_chinese_codepoint(codepoint) || is_japanese_codepoint(codepoint) || is_korean_codepoint(codepoint); } - static bool is_symbol_codepoint(sf::Uint32 codepoint) { + static bool is_symbol_codepoint(uint32_t codepoint) { // TODO: Add the remaining NotoSansSymbols2-Regular.ttf codepoints as well. // See codepoint ranges with: fc-query --format='%{charset}\n' /usr/share/fonts/noto/NotoSansSymbols2-Regular.ttf. return codepoint >= 0x2800 && codepoint <= 0x28FF; // Braille } - static size_t find_end_of_cjk(const sf::Uint32 *str, size_t size) { - for(size_t i = 0; i < size; ++i) { - if(!is_cjk_codepoint(str[i])) + static size_t find_end_of_cjk(const char *str, size_t size) { + for(size_t i = 0; i < size;) { + const unsigned char *cp = (const unsigned char*)&str[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + if(!is_cjk_codepoint(codepoint)) return i; + + i += clen; } return size; } - static size_t find_end_of_emoji(const sf::Uint32 *str, size_t size) { - for(size_t i = 0; i < size; ++i) { - if(!codepoint_is_emoji(str[i])) + static size_t find_end_of_emoji(const char *str, size_t size) { + for(size_t i = 0; i < size;) { + const unsigned char *cp = (const unsigned char*)&str[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + if(!codepoint_is_emoji(codepoint)) return i; + + i += clen; } return size; } - static size_t find_end_of_symbol(const sf::Uint32 *str, size_t size) { - for(size_t i = 0; i < size; ++i) { - if(!is_symbol_codepoint(str[i])) + static size_t find_end_of_symbol(const char *str, size_t size) { + for(size_t i = 0; i < size;) { + const unsigned char *cp = (const unsigned char*)&str[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + if(!is_symbol_codepoint(codepoint)) return i; + + i += clen; } return size; } - static size_t find_end_latin(const sf::Uint32 *str, size_t size) { - for(size_t i = 0; i < size; ++i) { - if(is_cjk_codepoint(str[i]) || codepoint_is_emoji(str[i]) || is_symbol_codepoint(str[i])) + static size_t find_end_latin(const char *str, size_t size) { + for(size_t i = 0; i < size;) { + const unsigned char *cp = (const unsigned char*)&str[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + if(is_cjk_codepoint(codepoint) || codepoint_is_emoji(codepoint) || is_symbol_codepoint(codepoint)) return i; + + i += clen; } return size; } @@ -283,30 +314,39 @@ namespace QuickMedia void Text::splitTextByFont() { textElements.clear(); size_t index = 0; - size_t size = str.getSize(); + size_t size = str.size(); while(index < size) { + const unsigned char *cp = (const unsigned char*)&str[index]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, size - index, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + size_t offset; TextElement::TextType text_type = TextElement::TextType::LATIN; - if(is_symbol_codepoint(str[index])) { + if(is_symbol_codepoint(codepoint)) { text_type = TextElement::TextType::SYMBOL; - offset = find_end_of_symbol(str.getData() + index + 1, size - index - 1); - } else if(is_cjk_codepoint(str[index])) { + offset = find_end_of_symbol(str.data() + index, size - index); + } else if(is_cjk_codepoint(codepoint)) { text_type = TextElement::TextType::CJK; - offset = find_end_of_cjk(str.getData() + index + 1, size - index - 1); - } else if(codepoint_is_emoji(str[index])) { + offset = find_end_of_cjk(str.data() + index, size - index); + } else if(codepoint_is_emoji(codepoint)) { text_type = TextElement::TextType::EMOJI; - offset = find_end_of_emoji(str.getData() + index + 1, size - index - 1); + offset = find_end_of_emoji(str.data() + index, size - index); } else { - offset = find_end_latin(str.getData() + index + 1, size - index - 1); + offset = find_end_latin(str.data() + index, size - index); } - textElements.push_back({ StringViewUtf32(str.getData() + index, offset + 1), TextElement::Type::TEXT }); + + textElements.push_back({ std::string_view(str.data() + index, offset), TextElement::Type::TEXT }); textElements.back().text_type = text_type; - index += 1 + offset; + index += offset; } } - float Text::font_get_real_height(sf::Font *font) { - return font->getGlyph('|', characterSize, false).bounds.height + std::floor(4.0f * ((float)characterSize / (float)14.0f)); + float Text::font_get_real_height(mgl::Font *font) { + return font->get_glyph('|').size.y + std::floor(4.0f * ((float)characterSize / (float)14.0f)); } float Text::get_text_quad_left_side(const VertexRef &vertex_ref) const { @@ -350,7 +390,7 @@ namespace QuickMedia } } - sf::Uint32 Text::get_vertex_codepoint(int index) const { + uint32_t Text::get_vertex_codepoint(int index) const { const int num_vertices = vertices_linear.size(); if(num_vertices == 0) { return 0; @@ -368,10 +408,7 @@ namespace QuickMedia splitTextByFont(); // TODO: Optimize if(highlight_urls) { - auto u8 = str.toUtf8(); - std::string *u8_str = (std::string*)&u8; - url_ranges = extract_urls(*u8_str); - convert_utf8_to_utf32_ranges(*u8_str, url_ranges); + url_ranges = extract_urls(str); } else { url_ranges.clear(); } @@ -384,81 +421,88 @@ namespace QuickMedia vertices_linear.clear(); for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) { vertices[i].clear(); - vertices[i].resize(0); + vertices[i].shrink_to_fit(); } - boundingBox = sf::FloatRect(); + boundingBox = mgl::FloatRect(mgl::vec2f(0.0f, 0.0f), mgl::vec2f(0.0f, 0.0f)); - sf::Font *latin_font; + mgl::Font *latin_font; if(bold_font) - latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD); + latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, characterSize); else - latin_font = FontLoader::get_font(FontLoader::FontType::LATIN); + latin_font = FontLoader::get_font(FontLoader::FontType::LATIN, characterSize); - const float latin_font_width = latin_font->getGlyph(' ', characterSize, false).advance; + const float latin_font_width = latin_font->get_glyph(' ').advance; const float hspace = latin_font_width + characterSpacing; const float vspace = font_get_real_height(latin_font); const float emoji_scale = vspace / 20.0f; - const sf::Color url_color = get_theme().url_text_color; + const mgl::Color url_color = get_theme().url_text_color; size_t url_range_index = 0; - sf::Vector2f glyphPos; - sf::Uint32 prevCodePoint = 0; + mgl::vec2f glyphPos; + uint32_t prevCodePoint = 0; // TODO: Only do this if dirtyText (then the Text object shouldn't be reset in Body. There should be a cleanup function in text instead) for(usize textElementIndex = 0; textElementIndex < textElements.size(); ++textElementIndex) { TextElement &textElement = textElements[textElementIndex]; - const sf::Font *ff = latin_font; + mgl::Font *ff = latin_font; int vertices_index = FONT_INDEX_LATIN; prevCodePoint = 0; if(textElement.text_type == TextElement::TextType::CJK) { - ff = FontLoader::get_font(FontLoader::FontType::CJK); + ff = FontLoader::get_font(FontLoader::FontType::CJK, characterSize); vertices_index = FONT_INDEX_CJK; } else if(textElement.text_type == TextElement::TextType::SYMBOL) { - ff = FontLoader::get_font(FontLoader::FontType::SYMBOLS); + ff = FontLoader::get_font(FontLoader::FontType::SYMBOLS, characterSize); vertices_index = FONT_INDEX_SYMBOLS; } else if(textElement.text_type == TextElement::TextType::EMOJI) { vertices_index = FONT_INDEX_EMOJI; - textElement.position = glyphPos; - sf::Color emoji_color(255, 255, 255, color.a); - for(size_t i = 0; i < textElement.text.size; ++i) + mgl::Color emoji_color(255, 255, 255, color.a); + for(size_t i = 0; i < textElement.text.size();) { - sf::Uint32 codePoint = textElement.text[i]; - int vertexStart = vertices[vertices_index].getVertexCount(); - EmojiRectangle emoji_rec = emoji_get_extents(codePoint); - + const unsigned char *cp = (const unsigned char*)&textElement.text[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, textElement.text.size() - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + int vertexStart = vertices[vertices_index].size(); + EmojiRectangle emoji_rec = emoji_get_extents(codepoint); + const float font_height_offset = std::floor(-vspace * 0.2f); - sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f); - sf::Vector2f vertexTopRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f); - sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y + font_height_offset + emoji_rec.height * emoji_scale * 0.5f); - sf::Vector2f vertexBottomRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset + std::floor(emoji_rec.height * emoji_scale) * 0.5f); + mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f); + mgl::vec2f vertexTopRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f); + mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y + font_height_offset + emoji_rec.height * emoji_scale * 0.5f); + mgl::vec2f vertexBottomRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset + std::floor(emoji_rec.height * emoji_scale) * 0.5f); - sf::Vector2f textureTopLeft(emoji_rec.x, emoji_rec.y); - sf::Vector2f textureTopRight(emoji_rec.x + emoji_rec.width, emoji_rec.y); - sf::Vector2f textureBottomLeft(emoji_rec.x, emoji_rec.y + emoji_rec.height); - sf::Vector2f textureBottomRight(emoji_rec.x + emoji_rec.width, emoji_rec.y + emoji_rec.height); + mgl::vec2f textureTopLeft(emoji_rec.x, emoji_rec.y); + mgl::vec2f textureTopRight(emoji_rec.x + emoji_rec.width, emoji_rec.y); + mgl::vec2f textureBottomLeft(emoji_rec.x, emoji_rec.y + emoji_rec.height); + mgl::vec2f textureBottomRight(emoji_rec.x + emoji_rec.width, emoji_rec.y + emoji_rec.height); - vertices[vertices_index].append({ vertexTopRight, emoji_color, textureTopRight }); - vertices[vertices_index].append({ vertexTopLeft, emoji_color, textureTopLeft }); - vertices[vertices_index].append({ vertexBottomLeft, emoji_color, textureBottomLeft }); - vertices[vertices_index].append({ vertexBottomLeft, emoji_color, textureBottomLeft }); - vertices[vertices_index].append({ vertexBottomRight, emoji_color, textureBottomRight }); - vertices[vertices_index].append({ vertexTopRight, emoji_color, textureTopRight }); + vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, emoji_color); + vertices[vertices_index].emplace_back(vertexTopLeft, textureTopLeft, emoji_color); + vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, emoji_color); + vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, emoji_color); + vertices[vertices_index].emplace_back(vertexBottomRight, textureBottomRight, emoji_color); + vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, emoji_color); glyphPos.x += std::floor(emoji_rec.width * emoji_scale) + characterSpacing; - vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); + vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint}); + + i += clen; } continue; } - //vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * textElement.text.size); // TODO: Precalculate - textElement.position = glyphPos; - for(size_t i = 0; i < textElement.text.size; ++i) + //vertices[vertices_index].resize(vertices[vertices_index].size() + 4 * textElement.text.size); // TODO: Precalculate + for(size_t i = 0; i < textElement.text.size();) { - sf::Color text_color = color; + mgl::Color text_color = color; if(url_range_index < url_ranges.size()) { - size_t string_offset = (textElement.text.data + i) - str.getData(); + size_t string_offset = (textElement.text.data() + i) - str.data(); if(string_offset >= url_ranges[url_range_index].start && string_offset < url_ranges[url_range_index].start + url_ranges[url_range_index].length) { text_color = url_color; text_color.a = color.a; @@ -467,96 +511,107 @@ namespace QuickMedia } } - sf::Uint32 codePoint = textElement.text[i]; + const unsigned char *cp = (const unsigned char*)&textElement.text[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, textElement.text.size() - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } // TODO: Make this work when combining multiple different fonts (for example latin and japanese). // For japanese we could use a hack, because all japanese characters are monospace (exception being half-width characters). - float kerning = ff->getKerning(prevCodePoint, codePoint, characterSize); - prevCodePoint = codePoint; + // TODO: + float kerning = ff->get_kerning(prevCodePoint, codepoint); + //float kerning = 0.0f; + prevCodePoint = codepoint; glyphPos.x += kerning; - int vertexStart = vertices[vertices_index].getVertexCount(); + int vertexStart = vertices[vertices_index].size(); - switch(codePoint) + i += clen; + switch(codepoint) { case ' ': { - sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace); - sf::Vector2f vertexTopRight(glyphPos.x + hspace, glyphPos.y - vspace); - sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y); - sf::Vector2f vertexBottomRight(glyphPos.x + hspace, glyphPos.y); - - vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexTopLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomRight, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() }); + mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace); + mgl::vec2f vertexTopRight(glyphPos.x + hspace, glyphPos.y - vspace); + mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y); + mgl::vec2f vertexBottomRight(glyphPos.x + hspace, glyphPos.y); + + vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexTopLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); glyphPos.x += hspace; - vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); - continue; + vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint}); + break; } case '\t': { const float char_width = hspace * TAB_WIDTH; - sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace); - sf::Vector2f vertexTopRight(glyphPos.x + char_width, glyphPos.y - vspace); - sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y); - sf::Vector2f vertexBottomRight(glyphPos.x + char_width, glyphPos.y); - - vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexTopLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomRight, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() }); + mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace); + mgl::vec2f vertexTopRight(glyphPos.x + char_width, glyphPos.y - vspace); + mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y); + mgl::vec2f vertexBottomRight(glyphPos.x + char_width, glyphPos.y); + + vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexTopLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); glyphPos.x += char_width; - vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); - continue; + vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint}); + break; } case '\n': { - sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace); - sf::Vector2f vertexTopRight(glyphPos.x, glyphPos.y - vspace); - sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y); - sf::Vector2f vertexBottomRight(glyphPos.x, glyphPos.y); - - vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexTopLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexBottomRight, sf::Color::Transparent, sf::Vector2f() }); - vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() }); + mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace); + mgl::vec2f vertexTopRight(glyphPos.x, glyphPos.y - vspace); + mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y); + mgl::vec2f vertexBottomRight(glyphPos.x, glyphPos.y); + + vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexTopLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexBottomRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); + vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0)); glyphPos.x = 0.0f; glyphPos.y += floor(vspace + lineSpacing); - vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); - continue; + vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint}); + break; } - } + default: { + mgl::FontGlyph glyph = ff->get_glyph(codepoint); - const sf::Glyph &glyph = ff->getGlyph(codePoint, characterSize, false); - - sf::Vector2f vertexTopLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top); - sf::Vector2f vertexTopRight(glyphPos.x + glyph.bounds.left + glyph.bounds.width, glyphPos.y + glyph.bounds.top); - sf::Vector2f vertexBottomLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top + glyph.bounds.height); - sf::Vector2f vertexBottomRight(glyphPos.x + glyph.bounds.left + glyph.bounds.width, glyphPos.y + glyph.bounds.top + glyph.bounds.height); - - sf::Vector2f textureTopLeft(glyph.textureRect.left, glyph.textureRect.top); - sf::Vector2f textureTopRight(glyph.textureRect.left + glyph.textureRect.width, glyph.textureRect.top); - sf::Vector2f textureBottomLeft(glyph.textureRect.left, glyph.textureRect.top + glyph.textureRect.height); - sf::Vector2f textureBottomRight(glyph.textureRect.left + glyph.textureRect.width, glyph.textureRect.top + glyph.textureRect.height); - - vertices[vertices_index].append({ vertexTopRight, text_color, textureTopRight }); - vertices[vertices_index].append({ vertexTopLeft, text_color, textureTopLeft }); - vertices[vertices_index].append({ vertexBottomLeft, text_color, textureBottomLeft }); - vertices[vertices_index].append({ vertexBottomLeft, text_color, textureBottomLeft }); - vertices[vertices_index].append({ vertexBottomRight, text_color, textureBottomRight }); - vertices[vertices_index].append({ vertexTopRight, text_color, textureTopRight }); - - glyphPos.x += glyph.advance + characterSpacing; - vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); + mgl::vec2f vertexTopLeft(glyphPos.x + glyph.position.x, glyphPos.y + glyph.position.y); + mgl::vec2f vertexTopRight(glyphPos.x + glyph.position.x + glyph.size.x, glyphPos.y + glyph.position.y); + mgl::vec2f vertexBottomLeft(glyphPos.x + glyph.position.x, glyphPos.y + glyph.position.y + glyph.size.y); + mgl::vec2f vertexBottomRight(glyphPos.x + glyph.position.x + glyph.size.x, glyphPos.y + glyph.position.y + glyph.size.y); + + mgl::vec2f textureTopLeft(glyph.texture_position.x, glyph.texture_position.y); + mgl::vec2f textureTopRight(glyph.texture_position.x + glyph.texture_size.x, glyph.texture_position.y); + mgl::vec2f textureBottomLeft(glyph.texture_position.x, glyph.texture_position.y + glyph.texture_size.y); + mgl::vec2f textureBottomRight(glyph.texture_position.x + glyph.texture_size.x, glyph.texture_position.y + glyph.texture_size.y); + + vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, text_color); + vertices[vertices_index].emplace_back(vertexTopLeft, textureTopLeft, text_color); + vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, text_color); + vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, text_color); + vertices[vertices_index].emplace_back(vertexBottomRight, textureBottomRight, text_color); + vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, text_color); + + glyphPos.x += glyph.advance + characterSpacing; + vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint}); + break; + } + } } } @@ -569,7 +624,7 @@ namespace QuickMedia for(int i = 0; i < (int)vertices_linear.size(); ++i) { VertexRef &vertex_ref = vertices_linear[i]; - sf::Vertex *vertex = &vertices[vertex_ref.vertices_index][vertex_ref.index]; + mgl::Vertex *vertex = &vertices[vertex_ref.vertices_index][vertex_ref.index]; for(int v = 0; v < 6; ++v) { vertex[v].position.x -= text_wrap_offset; vertex[v].position.y += text_offset_y; @@ -598,7 +653,7 @@ namespace QuickMedia float vertex_left_side = get_text_quad_left_side(vertices_linear[last_space_index + 1]); for(int j = last_space_index + 1; j <= i; ++j) { VertexRef &vertex_ref_wrap = vertices_linear[j]; - sf::Vertex *vertex = &vertices[vertex_ref_wrap.vertices_index][vertex_ref_wrap.index]; + mgl::Vertex *vertex = &vertices[vertex_ref_wrap.vertices_index][vertex_ref_wrap.index]; for(int v = 0; v < 6; ++v) { vertex[v].position.x -= vertex_left_side; vertex[v].position.y += line_height; @@ -621,24 +676,23 @@ namespace QuickMedia } } - boundingBox.width = 0.0f; + boundingBox.size.x = 0.0f; for(VertexRef &vertex_ref : vertices_linear) { - boundingBox.width = std::max(boundingBox.width, get_text_quad_right_side(vertex_ref)); + boundingBox.size.x = std::max(boundingBox.size.x, get_text_quad_right_side(vertex_ref)); } - boundingBox.height = num_lines * line_height; + boundingBox.size.y = num_lines * line_height; // TODO: Clear |vertices| somehow even with editable text for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) { - vertex_buffers[i].create(vertices[i].getVertexCount()); - if(vertices[i].getVertexCount() > 0) - vertex_buffers[i].update(&vertices[i][0], vertices[i].getVertexCount(), 0); + // TODO: Use VertexBuffer::Dynamic for editable text? + vertex_buffers[i].update(vertices[i].data(), vertices[i].size(), mgl::PrimitiveType::Triangles, mgl::VertexBuffer::Static); } //url_ranges.clear(); if(!editable) { for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) { vertices[i].clear(); - vertices[i].resize(0); + vertices[i].shrink_to_fit(); } vertices_linear.clear(); vertices_linear.shrink_to_fit(); @@ -649,17 +703,17 @@ namespace QuickMedia { assert(!dirty && !dirtyText); - sf::Font *latin_font; + mgl::Font *latin_font; if(bold_font) - latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD); + latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, characterSize); else - latin_font = FontLoader::get_font(FontLoader::FontType::LATIN); + latin_font = FontLoader::get_font(FontLoader::FontType::LATIN, characterSize); const float vspace = font_get_real_height(latin_font); if(vertices_linear.empty()) { caretIndex = 0; - caretPosition = sf::Vector2f(0.0f, floor(vspace)); + caretPosition = mgl::vec2f(0.0f, floor(vspace)); caret_offset_x = 0.0f; return; } @@ -749,7 +803,7 @@ namespace QuickMedia return num_vertices; } - static bool is_special_character(sf::Uint32 codepoint) { + static bool is_special_character(uint32_t codepoint) { return (codepoint <= 47) || (codepoint >= 58 && codepoint <= 64) || (codepoint >= 91 && codepoint <= 96) || (codepoint >= 123 && codepoint <= 127); } @@ -817,18 +871,56 @@ namespace QuickMedia return startIndex; } + // TODO: Optimize + size_t Text::get_string_index_from_caret_index(size_t caret_index) const { + size_t codepoint_index = 0; + for(size_t i = 0; i < str.size();) { + if(codepoint_index == caret_index) + return i; + + unsigned char *cp = (unsigned char*)&str[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, str.size() - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + i += clen; + ++codepoint_index; + } + return str.size(); + } + + static size_t utf8_get_length(const std::string &str) { + size_t codepoint_index = 0; + for(size_t i = 0; i < str.size();) { + unsigned char *cp = (unsigned char*)&str[i]; + uint32_t codepoint; + size_t clen; + if(!mgl::utf8_decode(cp, str.size() - i, &codepoint, &clen)) { + codepoint = *cp; + clen = 1; + } + + i += clen; + ++codepoint_index; + } + return codepoint_index; + } + // TODO: Optimize text editing by only processing the changed parts in updateGeometry. // TODO: Split text into lines and add to vertices list so the lines that are cut off are not visible. This is good when using the text and as text input // where there are a max number of rows shown at a time. - void Text::processEvent(const sf::Event &event) + void Text::processEvent(mgl::Window &window, const mgl::Event &event) { if(!editable) return; bool caretAtEnd = caretIndex == (int)vertices_linear.size(); - if(event.type == sf::Event::KeyPressed) + if(event.type == mgl::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Left && caretIndex > 0) + if(event.key.code == mgl::Keyboard::Left && caretIndex > 0) { if(event.key.control) caretMoveDirection = CaretMoveDirection::LEFT_WORD; @@ -836,7 +928,7 @@ namespace QuickMedia caretMoveDirection = CaretMoveDirection::LEFT; dirtyCaret = true; } - else if(event.key.code == sf::Keyboard::Right && !caretAtEnd) + else if(event.key.code == mgl::Keyboard::Right && !caretAtEnd) { if(event.key.control) caretMoveDirection = CaretMoveDirection::RIGHT_WORD; @@ -844,48 +936,60 @@ namespace QuickMedia caretMoveDirection = CaretMoveDirection::RIGHT; dirtyCaret = true; } - else if(event.key.code == sf::Keyboard::BackSpace && caretIndex > 0) + else if(event.key.code == mgl::Keyboard::Backspace && caretIndex > 0) { - str.erase(caretIndex - 1, 1); - --caretIndex; - dirty = true; - dirtyText = true; - dirtyCaret = true; + const size_t str_index = get_string_index_from_caret_index(caretIndex); + if(str_index > 0) { + const size_t codepoint_start = mgl::utf8_get_start_of_codepoint((const unsigned char*)str.c_str(), str.size(), str_index - 1); + str.erase(codepoint_start, str_index - codepoint_start); + --caretIndex; + dirty = true; + dirtyText = true; + dirtyCaret = true; + } } - else if(event.key.code == sf::Keyboard::Delete && !caretAtEnd) + else if(event.key.code == mgl::Keyboard::Delete && !caretAtEnd) { - str.erase(caretIndex, 1); + const size_t str_index = get_string_index_from_caret_index(caretIndex); + uint32_t decoded_codepoint = 0; + size_t decoded_length = 0; + mgl::utf8_decode((const unsigned char*)str.c_str() + str_index, str.size() - str_index, &decoded_codepoint, &decoded_length); + str.erase(str_index, decoded_length); dirty = true; dirtyText = true; } - else if(event.key.code == sf::Keyboard::D && event.key.control) + else if(event.key.code == mgl::Keyboard::D && event.key.control) { setString(""); } - else if(event.key.code == sf::Keyboard::Up) + else if(event.key.code == mgl::Keyboard::Up) { caretMoveDirection = CaretMoveDirection::UP; } - else if(event.key.code == sf::Keyboard::Down) + else if(event.key.code == mgl::Keyboard::Down) { caretMoveDirection = CaretMoveDirection::DOWN; } - else if(event.key.code == sf::Keyboard::Home) + else if(event.key.code == mgl::Keyboard::Home) { caretMoveDirection = CaretMoveDirection::HOME; } - else if(event.key.code == sf::Keyboard::End) + else if(event.key.code == mgl::Keyboard::End) { caretMoveDirection = CaretMoveDirection::END; } - else if(event.key.code == sf::Keyboard::Enter) + else if(event.key.code == mgl::Keyboard::Enter) { if(event.key.shift && !single_line_edit) { - if(caretAtEnd) + if(caretAtEnd) { str += '\n'; - else - str.insert(caretIndex, '\n'); + } else { + const size_t str_index = get_string_index_from_caret_index(caretIndex); + if(str_index > 0) { + str.insert(str_index, 1, '\n'); + } + } ++caretIndex; dirty = true; @@ -894,34 +998,38 @@ namespace QuickMedia } } } - else if(event.type == sf::Event::TextEntered) + else if(event.type == mgl::Event::TextEntered) { - if(event.text.unicode == 8 || event.text.unicode == 127) // backspace, del + if(event.text.codepoint == 8 || event.text.codepoint == 127) // backspace, del return; - sf::String stringToAdd; - if(event.text.unicode == 22) // ctrl+v + std::string stringToAdd; + if(event.text.codepoint == 22) // ctrl+v { - stringToAdd = sf::Clipboard::getString(); + stringToAdd = window.get_clipboard(); } - else if(event.text.unicode >= 32 || (event.text.unicode == '\t' && !single_line_edit)) - stringToAdd = event.text.unicode; + else if(event.text.codepoint >= 32 || (event.text.codepoint == '\t' && !single_line_edit)) + stringToAdd.assign(event.text.str, event.text.size); else return; - if(caretAtEnd) + if(caretAtEnd) { str += stringToAdd; - else - str.insert(caretIndex, stringToAdd); + } else { + const size_t str_index = get_string_index_from_caret_index(caretIndex); + if(str_index > 0 && str_index != std::string::npos) { + str.insert(str_index, stringToAdd); + } + } - caretIndex += stringToAdd.getSize(); + caretIndex += utf8_get_length(stringToAdd); dirty = true; dirtyText = true; dirtyCaret = true; } } - bool Text::draw(sf::RenderTarget &target) + bool Text::draw(mgl::Window &target) { updateGeometry(); @@ -931,35 +1039,34 @@ namespace QuickMedia caretMoveDirection = CaretMoveDirection::NONE; } - sf::Vector2f pos = position; + mgl::vec2f pos = position; FontLoader::FontType latin_font_type; if(bold_font) latin_font_type = FontLoader::FontType::LATIN_BOLD; else latin_font_type = FontLoader::FontType::LATIN; - sf::Font *latin_font = FontLoader::get_font(latin_font_type); + mgl::Font *latin_font = FontLoader::get_font(latin_font_type, characterSize); const float vspace = font_get_real_height(latin_font); pos.y += floor(vspace); // Origin is at bottom left, we want it to be at top left const FontLoader::FontType font_types[] = { latin_font_type, FontLoader::FontType::CJK, FontLoader::FontType::SYMBOLS }; for(size_t i = 0; i < FONT_INDEX_EMOJI; ++i) { - if(vertex_buffers[i].getVertexCount() == 0) + if(vertex_buffers[i].size() == 0) continue; - sf::Font *font = FontLoader::get_font(font_types[i]); - sf::RenderStates states; - states.transform.translate(pos); - states.texture = &font->getTexture(characterSize); - target.draw(vertex_buffers[i], states); + mgl::Font *font = FontLoader::get_font(font_types[i], characterSize); + mgl::Texture font_texture = font->get_texture(); + vertex_buffers[i].set_texture(&font_texture); + vertex_buffers[i].set_position(pos); + target.draw(vertex_buffers[i]); } - if(vertex_buffers[FONT_INDEX_EMOJI].getVertexCount() > 0) { - sf::RenderStates states; - states.transform.translate(pos); - states.texture = TextureLoader::get_texture("images/emoji.png"); - target.draw(vertex_buffers[FONT_INDEX_EMOJI], states); + if(vertex_buffers[FONT_INDEX_EMOJI].size() > 0) { + vertex_buffers[FONT_INDEX_EMOJI].set_texture(TextureLoader::get_texture("images/emoji.png", true)); + vertex_buffers[FONT_INDEX_EMOJI].set_position(pos); + target.draw(vertex_buffers[FONT_INDEX_EMOJI]); } if(!editable) return true; @@ -967,8 +1074,11 @@ namespace QuickMedia const float caret_margin = std::floor(2.0f * get_config().scale); - sf::RectangleShape caretRect(sf::Vector2f(std::floor(2.0f * get_config().scale), floor(vspace - caret_margin * 2.0f))); - caretRect.setPosition(floor(pos.x + caretPosition.x), floor(pos.y + caretPosition.y + caret_margin + std::floor(4.0f * get_config().scale))); + mgl::Rectangle caretRect(mgl::vec2f(0.0f, 0.0f), mgl::vec2f(std::floor(2.0f * get_config().scale), floor(vspace - caret_margin * 2.0f))); + caretRect.set_position(mgl::vec2f( + floor(pos.x + caretPosition.x), + floor(pos.y + caretPosition.y + caret_margin + std::floor(4.0f * get_config().scale)) + )); target.draw(caretRect); return true; } diff --git a/src/Theme.cpp b/src/Theme.cpp index 90e4b30..36c8ff7 100644 --- a/src/Theme.cpp +++ b/src/Theme.cpp @@ -32,7 +32,7 @@ namespace QuickMedia { return -1; } - static void parse_hex_set_color(const Json::Value &json_obj, const char *field_name, sf::Color &color) { + static void parse_hex_set_color(const Json::Value &json_obj, const char *field_name, mgl::Color &color) { const Json::Value &json_val = json_obj[field_name]; if(!json_val.isString()) { fprintf(stderr, "Warning: theme variable \"%s\" does not exists or is not a string\n", field_name); @@ -52,7 +52,7 @@ namespace QuickMedia { return; } - sf::Color new_color; + mgl::Color new_color; for(int i = 1; i < color_str_len; i += 2) { const int c1 = get_hex_value(color_str[i + 0]); const int c2 = get_hex_value(color_str[i + 1]); diff --git a/src/Utils.cpp b/src/Utils.cpp index d466bee..ca153ab 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -85,7 +85,7 @@ namespace QuickMedia { return (int64_t)time.tv_sec * 1000 + (int64_t)time.tv_nsec / 1000000; } - sf::Vector2f vec2f_floor(float x, float y) { - return sf::Vector2f(int(x), int(y)); + mgl::vec2f vec2f_floor(float x, float y) { + return mgl::vec2f(int(x), int(y)); } } diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp index 8020c3c..f9ae04d 100644 --- a/src/VideoPlayer.cpp +++ b/src/VideoPlayer.cpp @@ -16,11 +16,11 @@ #include <fcntl.h> #include <signal.h> -const int RETRY_TIME_MS = 500; -const int MAX_RETRIES_CONNECT = 1000; -const int READ_TIMEOUT_MS = 200; - namespace QuickMedia { + static const double RETRY_TIME_SEC = 0.5; + static const int MAX_RETRIES_CONNECT = 1000; + static const double READ_TIMEOUT_SEC = 0.2; + static std::string media_chapters_to_ffmetadata_chapters(const std::vector<MediaChapter> &chapters) { std::string result = ";FFMETADATA1\n\n"; for(size_t i = 0; i < chapters.size(); ++i) { @@ -126,7 +126,7 @@ namespace QuickMedia { return path; } - VideoPlayer::Error VideoPlayer::launch_video_process(const char *path, const char *audio_path, sf::WindowHandle _parent_window, const std::string &title, const std::string &start_time) { + VideoPlayer::Error VideoPlayer::launch_video_process(const char *path, const char *audio_path, mgl::WindowHandle _parent_window, const std::string &title, const std::string &start_time) { parent_window = _parent_window; if(!tmpnam(ipc_server_path)) { @@ -259,7 +259,7 @@ namespace QuickMedia { return Error::OK; } - VideoPlayer::Error VideoPlayer::load_video(const char *path, const char *audio_path, sf::WindowHandle _parent_window, bool use_youtube_dl, const std::string &title, const std::string &start_time, const std::vector<MediaChapter> &chapters) { + VideoPlayer::Error VideoPlayer::load_video(const char *path, const char *audio_path, mgl::WindowHandle _parent_window, bool use_youtube_dl, const std::string &title, const std::string &start_time, const std::vector<MediaChapter> &chapters) { // This check is to make sure we dont change window that the video belongs to. This is not a usecase we will have so // no need to support it for now at least. assert(parent_window == 0 || parent_window == _parent_window); @@ -326,12 +326,12 @@ namespace QuickMedia { if(find_window_tries == max_retries_find_window) return Error::FAIL_TO_FIND_WINDOW; - if(!connected_to_ipc && retry_timer.getElapsedTime().asMilliseconds() >= RETRY_TIME_MS) { + if(!connected_to_ipc && retry_timer.get_elapsed_time_seconds() >= RETRY_TIME_SEC) { retry_timer.restart(); if(connect(ipc_socket, (struct sockaddr*)&ipc_addr, sizeof(ipc_addr)) == -1) { ++connect_tries; if(connect_tries == MAX_RETRIES_CONNECT) { - fprintf(stderr, "Failed to connect to mpv ipc after %d seconds, last error: %s\n", (RETRY_TIME_MS * MAX_RETRIES_CONNECT)/1000, strerror(errno)); + fprintf(stderr, "Failed to connect to mpv ipc after %d seconds, last error: %s\n", (int)(RETRY_TIME_SEC * MAX_RETRIES_CONNECT), strerror(errno)); return Error::FAIL_TO_CONNECT_TIMEOUT; } } else { @@ -339,14 +339,14 @@ namespace QuickMedia { } } - if(connected_to_ipc && window_handle == 0 && retry_timer.getElapsedTime().asMilliseconds() >= RETRY_TIME_MS) { + if(connected_to_ipc && window_handle == 0 && retry_timer.get_elapsed_time_seconds() >= RETRY_TIME_SEC) { retry_timer.restart(); std::vector<Window> child_windows = get_child_window(display, parent_window); size_t num_children = child_windows.size(); if(num_children == 0) { ++find_window_tries; if(find_window_tries == max_retries_find_window) { - fprintf(stderr, "Failed to find mpv window after %d seconds\n", (RETRY_TIME_MS * max_retries_find_window)/1000); + fprintf(stderr, "Failed to find mpv window after %d seconds\n", (int)(RETRY_TIME_SEC * max_retries_find_window)); return Error::FAIL_TO_FIND_WINDOW_TIMEOUT; } } else if(num_children == 1) { @@ -462,7 +462,7 @@ namespace QuickMedia { if(err != Error::OK) return err; - sf::Clock read_timer; + mgl::Clock read_timer; expected_request_id = cmd_request_id; do { err = read_ipc_func(); @@ -471,7 +471,7 @@ namespace QuickMedia { if(response_data_status != ResponseDataStatus::NONE) break; - } while(read_timer.getElapsedTime().asMilliseconds() < READ_TIMEOUT_MS); + } while(read_timer.get_elapsed_time_seconds() < READ_TIMEOUT_SEC); if(response_data_status == ResponseDataStatus::OK) { if(request_response_data.type() == result_type) diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp index e44843d..13bc8c6 100644 --- a/src/gui/Button.cpp +++ b/src/gui/Button.cpp @@ -1,42 +1,43 @@ #include "../../include/gui/Button.hpp" #include "../../include/Theme.hpp" #include "../../include/Config.hpp" -#include <SFML/Graphics/RenderTarget.hpp> -#include <SFML/Window/Event.hpp> +#include <mglpp/system/FloatRect.hpp> +#include <mglpp/window/Window.hpp> +#include <mglpp/window/Event.hpp> #include <cmath> namespace QuickMedia { - static const float PADDING_Y = 10.0f; + static const float PADDING_Y = 5.0f; - Button::Button(const std::string &label, sf::Font *font, unsigned int character_size, float width, sf::Shader *rounded_rectangle_shader, float scale) : - label(label, *font, character_size * scale), - background(sf::Vector2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, rounded_rectangle_shader), + Button::Button(const std::string &label, mgl::Font *font, float width, mgl::Shader *rounded_rectangle_shader, float scale) : + label(label, *font), + background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().shade_color, rounded_rectangle_shader), scale(scale) { - background.set_size(sf::Vector2f(std::floor(width * scale), get_height())); - set_position(sf::Vector2f(0.0f, 0.0f)); + background.set_size(mgl::vec2f(std::floor(width * scale), get_height())); + set_position(mgl::vec2f(0.0f, 0.0f)); } - ButtonEvent Button::on_event(sf::Event &event) { + ButtonEvent Button::on_event(mgl::Event &event) { ButtonEvent performed_event = BUTTON_EVENT_NONE; - if(event.type == sf::Event::MouseMoved) { - if(sf::FloatRect(background.get_position(), background.get_size()).contains(event.mouseMove.x, event.mouseMove.y)) { + if(event.type == mgl::Event::MouseMoved) { + if(mgl::FloatRect(background.get_position(), background.get_size()).contains(mgl::vec2f(event.mouse_move.x, event.mouse_move.y))) { const int inc = 20; - background.set_color(sf::Color( + background.set_color(mgl::Color( std::min(255, (int)background_color.r + inc), std::min(255, (int)background_color.g + inc), std::min(255, (int)background_color.b + inc))); } else { background.set_color(background_color); } - } else if(event.type == sf::Event::MouseButtonPressed) { - if(event.mouseButton.button == sf::Mouse::Left && sf::FloatRect(background.get_position(), background.get_size()).contains(event.mouseButton.x, event.mouseButton.y)) { + } else if(event.type == mgl::Event::MouseButtonPressed) { + if(event.mouse_button.button == mgl::Mouse::Left && mgl::FloatRect(background.get_position(), background.get_size()).contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) { clicked_inside = true; } else { clicked_inside = false; } - } else if(event.type == sf::Event::MouseButtonReleased) { - if(clicked_inside && event.mouseButton.button == sf::Mouse::Left && sf::FloatRect(background.get_position(), background.get_size()).contains(event.mouseButton.x, event.mouseButton.y)) { + } else if(event.type == mgl::Event::MouseButtonReleased) { + if(clicked_inside && event.mouse_button.button == mgl::Mouse::Left && mgl::FloatRect(background.get_position(), background.get_size()).contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) { performed_event = BUTTON_EVENT_CLICKED; } clicked_inside = false; @@ -44,27 +45,27 @@ namespace QuickMedia { return performed_event; } - void Button::draw(sf::RenderTarget &target) { + void Button::draw(mgl::Window &target) { background.draw(target); target.draw(label); } - void Button::set_background_color(sf::Color color) { + void Button::set_background_color(mgl::Color color) { background_color = color; background.set_color(background_color); } - void Button::set_position(sf::Vector2f pos) { + void Button::set_position(mgl::vec2f pos) { background.set_position(pos); - const auto label_bounds = label.getLocalBounds(); - sf::Vector2f label_pos(pos + background.get_size() * 0.5f - sf::Vector2f(label_bounds.width * 0.5f, label_bounds.height * 0.5f) - sf::Vector2f(0.0f, 5.0f * scale)); + const auto label_bounds = label.get_bounds(); + mgl::vec2f label_pos(pos + background.get_size() * 0.5f - label_bounds.size * 0.5f - mgl::vec2f(0.0f, 5.0f * scale)); label_pos.x = std::floor(label_pos.x); label_pos.y = std::floor(label_pos.y); - label.setPosition(label_pos); + label.set_position(label_pos); } - sf::Vector2f Button::get_position() const { + mgl::vec2f Button::get_position() const { return background.get_position(); } @@ -72,7 +73,7 @@ namespace QuickMedia { return background.get_size().x; } - float Button::get_height() const { - return std::floor((PADDING_Y * 2.0f) * scale + label.getLocalBounds().height); + float Button::get_height() { + return std::floor((PADDING_Y * 2.0f) * scale + label.get_bounds().size.y); } }
\ No newline at end of file diff --git a/src/plugins/AniList.cpp b/src/plugins/AniList.cpp index 3559a81..b892229 100644 --- a/src/plugins/AniList.cpp +++ b/src/plugins/AniList.cpp @@ -262,6 +262,7 @@ query ($id: Int, $page: Int, $perPage: Int) { } enum class ThumbnailSize { + SMALLEST, MEDIUM, LARGE }; @@ -275,7 +276,7 @@ query ($id: Int, $page: Int, $perPage: Int) { } // TODO: Somehow get the correct thumbnail size? - static sf::Vector2i thumbnail_size_get_prediced_size(ThumbnailSize thumbnail_size) { + static mgl::vec2i thumbnail_size_get_prediced_size(ThumbnailSize thumbnail_size) { switch(thumbnail_size) { case ThumbnailSize::MEDIUM: return {100, 158}; case ThumbnailSize::LARGE: return {215, 304}; diff --git a/src/plugins/FileManager.cpp b/src/plugins/FileManager.cpp index a92f3c1..59d3a2a 100644 --- a/src/plugins/FileManager.cpp +++ b/src/plugins/FileManager.cpp @@ -116,10 +116,10 @@ namespace QuickMedia { body_item->thumbnail_is_local = true; if(is_regular_file) { body_item->thumbnail_url = get_resource_loader_root_path() + std::string("images/file.png"); - body_item->thumbnail_size = sf::Vector2i(18, 24); + body_item->thumbnail_size = mgl::vec2i(18, 24); } else { body_item->thumbnail_url = get_resource_loader_root_path() + std::string("images/folder.png"); - body_item->thumbnail_size = sf::Vector2i(24, 22); + body_item->thumbnail_size = mgl::vec2i(24, 22); } } diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp index 01c8546..c906c4d 100644 --- a/src/plugins/Fourchan.cpp +++ b/src/plugins/Fourchan.cpp @@ -353,11 +353,11 @@ namespace QuickMedia { body_item->thumbnail_url = fourchan_image_url + board_id + "/" + tim_str + "s.jpg"; body_item->url = fourchan_image_url + board_id + "/" + tim_str + ext_str; - sf::Vector2i thumbnail_size(64, 64); + mgl::vec2i thumbnail_size(64, 64); const Json::Value &tn_w = post["tn_w"]; const Json::Value &tn_h = post["tn_h"]; if(tn_w.isNumeric() && tn_h.isNumeric()) - thumbnail_size = sf::Vector2i(tn_w.asInt(), tn_h.asInt()); + thumbnail_size = mgl::vec2i(tn_w.asInt(), tn_h.asInt()); body_item->thumbnail_size = std::move(thumbnail_size); } @@ -439,11 +439,11 @@ namespace QuickMedia { // thumbnails always has .jpg extension even if they are gifs or webm. body_item->thumbnail_url = fourchan_image_url + board_id + "/" + std::to_string(tim.asInt64()) + "s.jpg"; - sf::Vector2i thumbnail_size(64, 64); + mgl::vec2i thumbnail_size(64, 64); const Json::Value &tn_w = thread["tn_w"]; const Json::Value &tn_h = thread["tn_h"]; if(tn_w.isNumeric() && tn_h.isNumeric()) - thumbnail_size = sf::Vector2i(tn_w.asInt() * 0.5, tn_h.asInt() * 0.5); + thumbnail_size = mgl::vec2i(tn_w.asInt() / 2, tn_h.asInt() / 2); body_item->thumbnail_size = std::move(thumbnail_size); } diff --git a/src/plugins/ImageBoard.cpp b/src/plugins/ImageBoard.cpp index b87e4bb..3b7850b 100644 --- a/src/plugins/ImageBoard.cpp +++ b/src/plugins/ImageBoard.cpp @@ -1,9 +1,8 @@ #include "../../plugins/ImageBoard.hpp" -#include <SFML/Window/Clipboard.hpp> namespace QuickMedia { - void ImageBoardThreadPage::copy_to_clipboard(const BodyItem *body_item) const { - sf::Clipboard::setString(sf::String::fromUtf8(body_item->get_description().begin(), body_item->get_description().end())); + void ImageBoardThreadPage::copy_to_clipboard(const BodyItem *body_item) { + set_clipboard(body_item->get_description()); } PluginResult ImageBoardThreadPage::login(const std::string &token, const std::string &pin, std::string &response_msg) { diff --git a/src/plugins/Info.cpp b/src/plugins/Info.cpp index 05efc44..2b520ac 100644 --- a/src/plugins/Info.cpp +++ b/src/plugins/Info.cpp @@ -5,7 +5,6 @@ #include "../../include/Program.hpp" #include "../../include/Notification.hpp" #include "../../include/Storage.hpp" -#include <SFML/Window/Clipboard.hpp> namespace QuickMedia { static const char *REVERSE_IMAGE_SEARCH_URL = "reverse-image-search://"; @@ -50,7 +49,7 @@ namespace QuickMedia { } } - void InfoPage::copy_to_clipboard(const BodyItem *body_item) const { + void InfoPage::copy_to_clipboard(const BodyItem *body_item) { std::string url; if(string_starts_with(body_item->url, REVERSE_IMAGE_SEARCH_URL)) { url = body_item->url.substr(strlen(REVERSE_IMAGE_SEARCH_URL)); @@ -61,7 +60,7 @@ namespace QuickMedia { } if(!url.empty()) - sf::Clipboard::setString(sf::String::fromUtf8(url.begin(), url.end())); + set_clipboard(url); } // static diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index c9d57d4..2689904 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -26,7 +26,7 @@ // Remove older messages (outside screen) to save memory. Reload them when the selected body item is the top/bottom one. namespace QuickMedia { - static const sf::Vector2i thumbnail_max_size(600, 337); + static const mgl::vec2i thumbnail_max_size(600, 337); static const char* SERVICE_NAME = "matrix"; static const char* OTHERS_ROOM_TAG = "tld.name.others"; // Filter without account data. TODO: We include pinned events but limit events to 1. That means if the last event is a pin, @@ -102,17 +102,17 @@ namespace QuickMedia { return std::abs(hash); } - sf::Color user_id_to_color(const std::string &user_id) { + mgl::Color user_id_to_color(const std::string &user_id) { const int num_colors = 8; - const sf::Color colors[num_colors] = { - sf::Color(54, 139, 214), - sf::Color(172, 59, 168), - sf::Color(3, 179, 129), - sf::Color(230, 79, 122), - sf::Color(255, 129, 45), - sf::Color(45, 194, 197), - sf::Color(92, 86, 245), - sf::Color(116, 209, 44) + const mgl::Color colors[num_colors] = { + mgl::Color(54, 139, 214), + mgl::Color(172, 59, 168), + mgl::Color(3, 179, 129), + mgl::Color(230, 79, 122), + mgl::Color(255, 129, 45), + mgl::Color(45, 194, 197), + mgl::Color(92, 86, 245), + mgl::Color(116, 209, 44) }; return colors[color_hash_code(user_id) % num_colors]; } @@ -374,7 +374,7 @@ namespace QuickMedia { body_item->thumbnail_url = room->get_avatar_url(); body_item->userdata = room; // Note: this has to be valid as long as the room list is valid! body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; - body_item->thumbnail_size = sf::Vector2i(32, 32); + body_item->thumbnail_size = mgl::vec2i(32, 32); room->body_item = body_item; room_body_item_by_room[room] = body_item; rooms_page->add_body_item(body_item); @@ -440,7 +440,7 @@ namespace QuickMedia { body_item->url = room_id; body_item->thumbnail_url = invite.room_avatar_url; body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; - body_item->thumbnail_size = sf::Vector2i(32, 32); + body_item->thumbnail_size = mgl::vec2i(32, 32); body_item->set_timestamp(invite.timestamp); invites_page->add_body_item(std::move(body_item)); if(invite.new_invite) { @@ -908,7 +908,7 @@ namespace QuickMedia { if(user_info.avatar_url) body_item->thumbnail_url = user_info.avatar_url.value(); body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; - body_item->thumbnail_size = sf::Vector2i(32, 32); + body_item->thumbnail_size = mgl::vec2i(32, 32); users_body->append_item(std::move(body_item)); } @@ -1040,7 +1040,7 @@ namespace QuickMedia { } body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; - body_item->thumbnail_size = sf::Vector2i(32, 32); + body_item->thumbnail_size = mgl::vec2i(32, 32); body_item->thumbnail_url = notification.room->get_avatar_url(); auto extra_data = std::make_shared<NotificationsExtraData>(); @@ -1957,7 +1957,7 @@ namespace QuickMedia { } } - static bool message_content_extract_thumbnail_size(const rapidjson::Value &content_json, sf::Vector2i &thumbnail_size) { + static bool message_content_extract_thumbnail_size(const rapidjson::Value &content_json, mgl::vec2i &thumbnail_size) { const rapidjson::Value &info_json = GetMember(content_json, "info"); if(!info_json.IsObject()) return false; @@ -4424,7 +4424,7 @@ namespace QuickMedia { body_item->thumbnail_url = std::move(avatar_url); } body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; - body_item->thumbnail_size = sf::Vector2i(32, 32); + body_item->thumbnail_size = mgl::vec2i(32, 32); result_items.push_back(std::move(body_item)); } @@ -4736,7 +4736,7 @@ namespace QuickMedia { } void Matrix::update() { - sf::Clock timer; + mgl::Clock timer; std::optional<std::function<void()>> task; while((task = ui_thread_tasks.pop_if_available()) != std::nullopt) { task.value()(); diff --git a/src/plugins/MediaGeneric.cpp b/src/plugins/MediaGeneric.cpp index dd70efc..2b88fdb 100644 --- a/src/plugins/MediaGeneric.cpp +++ b/src/plugins/MediaGeneric.cpp @@ -37,7 +37,7 @@ namespace QuickMedia { } } - static PluginResult fetch_page_results(const std::string &url, const std::string &website_url, const std::vector<MediaTextQuery> &text_queries, const std::vector<MediaThumbnailQuery> &thumbnail_queries, sf::Vector2i thumbnail_max_size, MediaRelatedCustomHandler *custom_handler, BodyItems &result_items, bool cloudflare_bypass, const std::vector<CommandArg> &extra_commands) { + static PluginResult fetch_page_results(const std::string &url, const std::string &website_url, const std::vector<MediaTextQuery> &text_queries, const std::vector<MediaThumbnailQuery> &thumbnail_queries, mgl::vec2i thumbnail_max_size, MediaRelatedCustomHandler *custom_handler, BodyItems &result_items, bool cloudflare_bypass, const std::vector<CommandArg> &extra_commands) { std::vector<CommandArg> args = extra_commands; if(!website_url.empty()) args.push_back({ "-H", "referer: " + website_url }); @@ -118,7 +118,7 @@ namespace QuickMedia { } } - MediaGenericSearchPage::MediaGenericSearchPage(Program *program, const char *website_url, sf::Vector2i thumbnail_max_size, bool cloudflare_bypass, std::vector<CommandArg> extra_commands) : + MediaGenericSearchPage::MediaGenericSearchPage(Program *program, const char *website_url, mgl::vec2i thumbnail_max_size, bool cloudflare_bypass, std::vector<CommandArg> extra_commands) : Page(program), website_url(website_url ? website_url : ""), thumbnail_max_size(thumbnail_max_size), cloudflare_bypass(cloudflare_bypass), extra_commands(std::move(extra_commands)) { if(!this->website_url.empty()) { diff --git a/src/plugins/MyAnimeList.cpp b/src/plugins/MyAnimeList.cpp index a4d1d30..ee5d4fd 100644 --- a/src/plugins/MyAnimeList.cpp +++ b/src/plugins/MyAnimeList.cpp @@ -7,7 +7,7 @@ namespace QuickMedia { // Returns {0, 0} if unknown - static sf::Vector2i thumbnail_url_get_resolution(const std::string &url) { + static mgl::vec2i thumbnail_url_get_resolution(const std::string &url) { const size_t index = url.find("/r/"); if(index == std::string::npos) return {0, 0}; @@ -22,7 +22,7 @@ namespace QuickMedia { if(size_end_index == std::string::npos) return {0, 0}; - sf::Vector2i size; + mgl::vec2i size; if(!to_num(url.c_str() + width_index, (x_index - width_index), size.x) || !to_num(url.c_str() + height_index, (size_end_index - height_index), size.y)) return {0, 0}; diff --git a/src/plugins/NyaaSi.cpp b/src/plugins/NyaaSi.cpp index f4c0560..a110c57 100644 --- a/src/plugins/NyaaSi.cpp +++ b/src/plugins/NyaaSi.cpp @@ -274,10 +274,10 @@ namespace QuickMedia { body_item->set_description(std::move(description)); body_item->url = "https://" + domain + std::move(view_url); if(is_trusted) - body_item->set_title_color(sf::Color(43, 255, 47)); + body_item->set_title_color(mgl::Color(43, 255, 47)); else if(is_remake) - body_item->set_title_color(sf::Color(255, 45, 47)); - body_item->thumbnail_size = sf::Vector2i(80, 28); + body_item->set_title_color(mgl::Color(255, 45, 47)); + body_item->thumbnail_size = mgl::vec2i(80, 28); result_items.push_back(std::move(body_item)); } @@ -457,7 +457,7 @@ namespace QuickMedia { QuickMediaStringView src = quickmedia_html_node_get_attribute_value(node, "src"); if(src.data && item_data->index < item_data->body_items->size()) { (*item_data->body_items)[item_data->index]->thumbnail_url.assign(src.data, src.size); - (*item_data->body_items)[item_data->index]->thumbnail_size = sf::Vector2i(120, 120); + (*item_data->body_items)[item_data->index]->thumbnail_size = mgl::vec2i(120, 120); item_data->index++; } return 0; diff --git a/src/plugins/Page.cpp b/src/plugins/Page.cpp index 6cf3790..4ac2a9f 100644 --- a/src/plugins/Page.cpp +++ b/src/plugins/Page.cpp @@ -3,7 +3,6 @@ #include "../../include/Theme.hpp" #include "../../include/Storage.hpp" #include "../../include/QuickMedia.hpp" -#include <SFML/Window/Clipboard.hpp> #include <json/reader.h> namespace QuickMedia { @@ -45,7 +44,11 @@ namespace QuickMedia { return program->load_manga_content_storage(service_name, manga_title, manga_url, manga_id); } - void Page::copy_to_clipboard(const BodyItem *body_item) const { + void Page::set_clipboard(const std::string &str) { + program->set_clipboard(str); + } + + void Page::copy_to_clipboard(const BodyItem *body_item) { std::string title = body_item->get_title(); std::string author = body_item->get_author(); std::string description = body_item->get_description(); @@ -65,7 +68,7 @@ namespace QuickMedia { } if(!clipboard.empty()) - sf::Clipboard::setString(sf::String::fromUtf8(clipboard.begin(), clipboard.end())); + program->set_clipboard(clipboard); } PluginResult BookmarksPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) { diff --git a/src/plugins/Saucenao.cpp b/src/plugins/Saucenao.cpp index b99c028..064bb11 100644 --- a/src/plugins/Saucenao.cpp +++ b/src/plugins/Saucenao.cpp @@ -136,7 +136,7 @@ namespace QuickMedia { QuickMediaStringView image_url = data_src.data ? data_src : src; if(image_url.data && item_data->index < item_data->body_items->size()) { (*item_data->body_items)[item_data->index]->thumbnail_url.assign(image_url.data, image_url.size); - (*item_data->body_items)[item_data->index]->thumbnail_size = sf::Vector2i(150, 147); + (*item_data->body_items)[item_data->index]->thumbnail_size = mgl::vec2i(150, 147); item_data->index++; } return 0; diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index 7a5f0d5..8dabd2d 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -542,7 +542,7 @@ namespace QuickMedia { body_item->url = "https://www.youtube.com/watch?v=" + video_id_str; body_item->thumbnail_url = "https://img.youtube.com/vi/" + video_id_str + "/mqdefault.jpg"; - body_item->thumbnail_size = sf::Vector2i(192, 108); + body_item->thumbnail_size = mgl::vec2i(192, 108); added_videos.insert(video_id_str); return body_item; @@ -606,7 +606,7 @@ namespace QuickMedia { body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; body_item->thumbnail_size.x = thumbnail->width; body_item->thumbnail_size.y = thumbnail->height; - body_item->thumbnail_size = clamp_to_size(body_item->thumbnail_size, sf::Vector2i(136, 136)); + body_item->thumbnail_size = clamp_to_size(body_item->thumbnail_size, mgl::vec2i(136, 136)); } return body_item; } @@ -1140,7 +1140,7 @@ namespace QuickMedia { const Json::Value &author_is_channel_owner_json = comment_renderer_json["authorIsChannelOwner"]; if(author_is_channel_owner_json.isBool() && author_is_channel_owner_json.asBool()) - body_item->set_author_color(sf::Color(150, 255, 150)); + body_item->set_author_color(mgl::Color(150, 255, 150)); std::optional<std::string> comment = yt_json_get_text(comment_renderer_json, "contentText"); if(comment) @@ -1781,7 +1781,7 @@ namespace QuickMedia { body_item->set_description_color(get_theme().faded_text_color); body_item->url = "https://www.youtube.com/watch?v=" + subscription_entry.video_id; body_item->thumbnail_url = "https://img.youtube.com/vi/" + subscription_entry.video_id + "/mqdefault.jpg"; - body_item->thumbnail_size = sf::Vector2i(192, 108); + body_item->thumbnail_size = mgl::vec2i(192, 108); results.push_back({std::move(body_item), subscription_entry.published}); } return results; diff --git a/src/plugins/youtube/YoutubeMediaProxy.cpp b/src/plugins/youtube/YoutubeMediaProxy.cpp index 4fe6c74..913fb77 100644 --- a/src/plugins/youtube/YoutubeMediaProxy.cpp +++ b/src/plugins/youtube/YoutubeMediaProxy.cpp @@ -7,6 +7,7 @@ #include <string.h> #include <errno.h> #include <signal.h> +#include <inttypes.h> #include <assert.h> #include <unistd.h> @@ -222,7 +223,7 @@ namespace QuickMedia { return 0; int64_t start_range = 0; - if(sscanf(range.c_str(), " bytes=%lld", &start_range) != 1) + if(sscanf(range.c_str(), " bytes=%" PRId64, &start_range) != 1) return 0; return start_range; |