Compare commits
23 Commits
87c58f2bb5
...
fe70e90973
Author | SHA1 | Date |
---|---|---|
![]() |
fe70e90973 | |
![]() |
897978e955 | |
![]() |
e16e3f9a61 | |
![]() |
3f79aa23b4 | |
![]() |
0b7f9541d0 | |
![]() |
f92f174450 | |
![]() |
e18a4d04b4 | |
![]() |
2b5cd96cb1 | |
![]() |
d117614c00 | |
![]() |
9777e8e76b | |
![]() |
f1f147965b | |
![]() |
a33368b102 | |
![]() |
25c805be99 | |
![]() |
0b8301ff97 | |
![]() |
7b19192134 | |
![]() |
6d8f40c245 | |
![]() |
32e135e6a9 | |
![]() |
3edb5accca | |
![]() |
2e006d9787 | |
![]() |
24f2981e54 | |
![]() |
714271c22f | |
![]() |
0c46205ba7 | |
![]() |
5078a63084 |
|
@ -0,0 +1,35 @@
|
|||
# GUPR8P - Shadow the Hedgehog: Reloaded
|
||||
|
||||
[Gecko]
|
||||
$No Idle ChaosPowers Music v2 [dreamsyntax]
|
||||
040A4FF0 60000000
|
||||
040A4D84 60000000
|
||||
*Disables the jingle when Shadow is in HeroShadow or DarkShadow state. Chaos Powers (Blast/Control) will still play their special music/sounds. After the Chaos Power completes, the original stage music will resume instead of restarting from the beginning.
|
||||
|
||||
$Mute music and cutscenes
|
||||
0458F99C 00000000
|
||||
0458FA90 00000000
|
||||
*Mutes all adx tracks (including cutscenes).
|
||||
|
||||
$Skip Loading Any Events In Story Mode / Last Story [dreamsyntax]
|
||||
0425D460 60000000
|
||||
*Makes StageSequenceManager skip all events/sfd. For example pressing Last Story will immediately start The Last Way instead of playing the multiple events.
|
||||
|
||||
$No Subtitles [LimblessVector]
|
||||
045E1B9C 00000000
|
||||
*Voice lines will play, but no subtitles will render to the screen.
|
||||
*This includes pause text and rank info on select mode.
|
||||
|
||||
$Restore Partner Intro Cutscene Behavior [dreamsyntax]
|
||||
04073E10 4082004C
|
||||
*Restore the camera event on first partner meet.
|
||||
|
||||
$Force Restart on Death (Hold L) [dreamsyntax]
|
||||
C2204854 00000005
|
||||
3C808057 60845D5E
|
||||
88840000 2C040010
|
||||
38800008 41820008
|
||||
40820008 38800007
|
||||
60000000 00000000
|
||||
*QoL code for speedruns.
|
||||
*Hold L to force a restart on death.
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2021 Omar Cornut
|
||||
Copyright (c) 2014-2025 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -9,64 +9,73 @@
|
|||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/Assert.h"
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
#define IM_ASSERT(_EXPR) ASSERT(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
|
@ -77,6 +86,15 @@
|
|||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
@ -107,7 +125,7 @@
|
|||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
@ -115,10 +133,10 @@
|
|||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -2,8 +2,11 @@
|
|||
// This is a slightly modified version of stb_textedit.h 1.14.
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||
// Development of this library was sponsored by RAD Game Tools
|
||||
|
@ -30,7 +33,7 @@
|
|||
// DEPENDENCIES
|
||||
//
|
||||
// Uses the C runtime function 'memmove', which you can override
|
||||
// by defining STB_TEXTEDIT_memmove before the implementation.
|
||||
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
|
||||
// Uses no other functions. Performs no runtime allocations.
|
||||
//
|
||||
//
|
||||
|
@ -40,7 +43,7 @@
|
|||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
|
||||
// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
|
||||
// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
|
||||
// 1.9 (2016-08-27) customizable move-by-word
|
||||
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
|
||||
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
|
||||
|
@ -208,6 +211,7 @@
|
|||
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
|
||||
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
|
||||
//
|
||||
// Each of these functions potentially updates the string and updates the
|
||||
// state.
|
||||
|
@ -242,7 +246,12 @@
|
|||
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
|
||||
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
|
||||
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
|
||||
// anything other type you wante before including.
|
||||
// anything other type you want before including.
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: [DEAR IMGUI] added 2024-09
|
||||
// call this to text inputs sent to the textfield.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
|
@ -274,8 +283,8 @@
|
|||
////
|
||||
////
|
||||
|
||||
#ifndef INCLUDE_STB_TEXTEDIT_H
|
||||
#define INCLUDE_STB_TEXTEDIT_H
|
||||
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
|
||||
#define INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -286,38 +295,38 @@
|
|||
// and undo state.
|
||||
//
|
||||
|
||||
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_CHARTYPE
|
||||
#define STB_TEXTEDIT_CHARTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_CHARTYPE
|
||||
#define IMSTB_TEXTEDIT_CHARTYPE int
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_POSITIONTYPE
|
||||
#define STB_TEXTEDIT_POSITIONTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
|
||||
#define IMSTB_TEXTEDIT_POSITIONTYPE int
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
STB_TEXTEDIT_POSITIONTYPE where;
|
||||
STB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
STB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE where;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
int char_storage;
|
||||
} StbUndoRecord;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
short undo_point, redo_point;
|
||||
int undo_char_point, redo_char_point;
|
||||
} StbUndoState;
|
||||
|
||||
typedef struct
|
||||
typedef struct STB_TexteditState
|
||||
{
|
||||
/////////////////////
|
||||
//
|
||||
|
@ -371,7 +380,7 @@ typedef struct
|
|||
float ymin,ymax; // height of row above and below baseline
|
||||
int num_chars;
|
||||
} StbTexteditRow;
|
||||
#endif //INCLUDE_STB_TEXTEDIT_H
|
||||
#endif //INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -384,11 +393,11 @@ typedef struct
|
|||
|
||||
// implementation isn't include-guarded, since it might have indirectly
|
||||
// included just the "header" portion
|
||||
#ifdef STB_TEXTEDIT_IMPLEMENTATION
|
||||
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
#ifndef STB_TEXTEDIT_memmove
|
||||
#ifndef IMSTB_TEXTEDIT_memmove
|
||||
#include <string.h>
|
||||
#define STB_TEXTEDIT_memmove memmove
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -398,7 +407,7 @@ typedef struct
|
|||
//
|
||||
|
||||
// traverse the layout to locate the nearest character to a display position
|
||||
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
|
@ -437,13 +446,13 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
|||
if (x < r.x1) {
|
||||
// search characters in row for one that straddles 'x'
|
||||
prev_x = r.x0;
|
||||
for (k=0; k < r.num_chars; ++k) {
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
return k+i+1;
|
||||
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
|
||||
}
|
||||
prev_x += w;
|
||||
}
|
||||
|
@ -458,7 +467,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
|||
}
|
||||
|
||||
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
|
||||
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
|
@ -476,7 +485,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
|||
}
|
||||
|
||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
int p = 0;
|
||||
|
||||
|
@ -502,11 +511,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
|||
//
|
||||
|
||||
// forward declarations
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -518,7 +527,7 @@ typedef struct
|
|||
|
||||
// find the x/y location of a character, and remember info about the previous row in
|
||||
// case we get a move-up event (for page up, we'll have to rescan)
|
||||
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int prev_start = 0;
|
||||
|
@ -549,7 +558,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
|||
i += r.num_chars;
|
||||
find->y += r.baseline_y_delta;
|
||||
if (i == z) // [DEAR IMGUI]
|
||||
{
|
||||
r.num_chars = 0; // [DEAR IMGUI]
|
||||
break; // [DEAR IMGUI]
|
||||
}
|
||||
}
|
||||
|
||||
find->first_char = first = i;
|
||||
|
@ -559,14 +571,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
|||
|
||||
// now scan to find xpos
|
||||
find->x = r.x0;
|
||||
for (i=0; first+i < n; ++i)
|
||||
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
|
||||
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
|
||||
}
|
||||
|
||||
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
|
||||
|
||||
// make the selection/cursor state valid if client altered the string
|
||||
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
|
@ -580,7 +592,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
|||
}
|
||||
|
||||
// delete characters while updating undo
|
||||
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||
{
|
||||
stb_text_makeundo_delete(str, state, where, len);
|
||||
STB_TEXTEDIT_DELETECHARS(str, where, len);
|
||||
|
@ -588,7 +600,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
|||
}
|
||||
|
||||
// delete the section
|
||||
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
stb_textedit_clamp(str, state);
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
|
@ -625,7 +637,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
|
|||
}
|
||||
|
||||
// move cursor to last character of selection
|
||||
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
stb_textedit_sortselection(state);
|
||||
|
@ -636,14 +648,25 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
|
|||
}
|
||||
}
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
||||
#endif
|
||||
|
||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
|
||||
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
{
|
||||
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
|
||||
}
|
||||
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
|
@ -658,7 +681,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
|||
#endif
|
||||
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
|
||||
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
|
@ -685,7 +708,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
|
|||
}
|
||||
|
||||
// API cut: delete selection
|
||||
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
|
@ -696,7 +719,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||
}
|
||||
|
||||
// API paste: replace existing selection with passed-in text
|
||||
static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
{
|
||||
// if there's a selection, the paste should delete it
|
||||
stb_textedit_clamp(str, state);
|
||||
|
@ -716,36 +739,44 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
|
|||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
// can't add newline in single-line mode
|
||||
if (text[0] == '\n' && state->single_line)
|
||||
return;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API key: process a keyboard input
|
||||
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
{
|
||||
retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
|
||||
|
||||
// can't add newline in single-line mode
|
||||
if (c == '\n' && state->single_line)
|
||||
break;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, 1);
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
stb_textedit_text(str, state, &ch, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -771,7 +802,7 @@ retry:
|
|||
stb_textedit_move_to_first(state);
|
||||
else
|
||||
if (state->cursor > 0)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
|
@ -780,7 +811,7 @@ retry:
|
|||
if (STB_TEXT_HAS_SELECTION(state))
|
||||
stb_textedit_move_to_last(str, state);
|
||||
else
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
@ -790,7 +821,7 @@ retry:
|
|||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection left
|
||||
if (state->select_end > 0)
|
||||
--state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
@ -840,7 +871,7 @@ retry:
|
|||
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection right
|
||||
++state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
|
@ -889,14 +920,14 @@ retry:
|
|||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
|
@ -951,14 +982,14 @@ retry:
|
|||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
|
@ -986,7 +1017,7 @@ retry:
|
|||
else {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->cursor < n)
|
||||
stb_textedit_delete(str, state, state->cursor, 1);
|
||||
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
@ -998,8 +1029,9 @@ retry:
|
|||
else {
|
||||
stb_textedit_clamp(str, state);
|
||||
if (state->cursor > 0) {
|
||||
stb_textedit_delete(str, state, state->cursor-1, 1);
|
||||
--state->cursor;
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
stb_textedit_delete(str, state, prev, state->cursor - prev);
|
||||
state->cursor = prev;
|
||||
}
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
|
@ -1109,8 +1141,8 @@ retry:
|
|||
|
||||
static void stb_textedit_flush_redo(StbUndoState *state)
|
||||
{
|
||||
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
}
|
||||
|
||||
// discard the oldest entry in the undo list
|
||||
|
@ -1122,13 +1154,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
|||
int n = state->undo_rec[0].insert_length, i;
|
||||
// delete n characters from all other records
|
||||
state->undo_char_point -= n;
|
||||
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||
for (i=0; i < state->undo_point; ++i)
|
||||
if (state->undo_rec[i].char_storage >= 0)
|
||||
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
|
||||
}
|
||||
--state->undo_point;
|
||||
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1138,7 +1170,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
|||
// fill up even though the undo buffer didn't
|
||||
static void stb_textedit_discard_redo(StbUndoState *state)
|
||||
{
|
||||
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||
|
||||
if (state->redo_point <= k) {
|
||||
// if the k'th undo state has characters, clean those up
|
||||
|
@ -1146,7 +1178,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
|
|||
int n = state->undo_rec[k].insert_length, i;
|
||||
// move the remaining redo character data to the end of the buffer
|
||||
state->redo_char_point += n;
|
||||
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||
// adjust the position of all the other records to account for above memmove
|
||||
for (i=state->redo_point; i < k; ++i)
|
||||
if (state->undo_rec[i].char_storage >= 0)
|
||||
|
@ -1154,12 +1186,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
|
|||
}
|
||||
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
||||
// [DEAR IMGUI]
|
||||
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
||||
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
||||
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
|
||||
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
|
||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
|
||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
|
||||
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
||||
|
||||
// now move redo_point to point to the new one
|
||||
++state->redo_point;
|
||||
|
@ -1173,32 +1205,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
|
|||
|
||||
// if we have no free records, we have to make room, by sliding the
|
||||
// existing records down
|
||||
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
// if the characters to store won't possibly fit in the buffer, we can't undo
|
||||
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
state->undo_point = 0;
|
||||
state->undo_char_point = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if we don't have enough free characters in the buffer, we have to make room
|
||||
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
|
||||
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
return &state->undo_rec[state->undo_point++];
|
||||
}
|
||||
|
||||
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||
{
|
||||
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->where = pos;
|
||||
r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
|
||||
if (insert_len == 0) {
|
||||
r->char_storage = -1;
|
||||
|
@ -1210,7 +1242,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
|
|||
}
|
||||
}
|
||||
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
StbUndoState *s = &state->undostate;
|
||||
StbUndoRecord u, *r;
|
||||
|
@ -1237,7 +1269,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||
// characters stored for *undoing* don't leave room for redo
|
||||
// if the last is true, we have to bail
|
||||
|
||||
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
// the undo records take up too much character space; there's no space to store the redo characters
|
||||
r->insert_length = 0;
|
||||
} else {
|
||||
|
@ -1246,7 +1278,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||
// there's definitely room to store the characters eventually
|
||||
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
|
||||
// should never happen:
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
// there's currently not enough room, so discard a redo record
|
||||
stb_textedit_discard_redo(s);
|
||||
|
@ -1278,11 +1310,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
|||
s->redo_point--;
|
||||
}
|
||||
|
||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
StbUndoState *s = &state->undostate;
|
||||
StbUndoRecord *u, r;
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
|
||||
// we need to do two things: apply the redo record, and create an undo record
|
||||
|
@ -1334,20 +1366,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
|
|||
stb_text_createundo(&state->undostate, where, 0, length);
|
||||
}
|
||||
|
||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||
{
|
||||
int i;
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||
if (p) {
|
||||
for (i=0; i < length; ++i)
|
||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||
}
|
||||
}
|
||||
|
||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||
{
|
||||
int i;
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||
if (p) {
|
||||
for (i=0; i < old_length; ++i)
|
||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||
|
@ -1359,8 +1391,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
|
|||
{
|
||||
state->undostate.undo_point = 0;
|
||||
state->undostate.undo_char_point = 0;
|
||||
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->select_end = state->select_start = 0;
|
||||
state->cursor = 0;
|
||||
state->has_preferred_x = 0;
|
||||
|
@ -1383,16 +1415,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
|
|||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||
{
|
||||
return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
|
||||
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif//STB_TEXTEDIT_IMPLEMENTATION
|
||||
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
|
|
|
@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
|
|||
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
|
||||
// If skip != 0, this tells stb_truetype to skip any codepoints for which
|
||||
// there is no corresponding glyph. If skip=0, which is the default, then
|
||||
// codepoints without a glyph recived the font's "missing character" glyph,
|
||||
// codepoints without a glyph received the font's "missing character" glyph,
|
||||
// typically an empty box by convention.
|
||||
|
||||
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
|
||||
|
|
|
@ -8,8 +8,15 @@
|
|||
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_stdlib.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
struct InputTextCallback_UserData
|
||||
{
|
||||
std::string* Str;
|
||||
|
@ -73,3 +80,9 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string*
|
|||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ImGui
|
||||
|
@ -19,3 +21,5 @@ namespace ImGui
|
|||
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
}
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit cc5e1daa5c7f2335a9460ae79c829011dc5cef2d
|
||||
Subproject commit 18c72431f8265e2b0b5378a3a73d8a883b2175ff
|
|
@ -3,8 +3,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common
|
||||
|
@ -14,39 +14,99 @@ namespace Common
|
|||
template <typename T, size_t MaxSize>
|
||||
class SmallVector final
|
||||
{
|
||||
static_assert(std::is_standard_layout_v<T> == true, "Type must be a standard layout type");
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
SmallVector() = default;
|
||||
explicit SmallVector(size_t size) : m_size(size) {}
|
||||
explicit SmallVector(size_t new_size) { resize(new_size); }
|
||||
|
||||
void push_back(const T& x) { m_array[m_size++] = x; }
|
||||
void push_back(T&& x) { m_array[m_size++] = std::move(x); }
|
||||
~SmallVector() { clear(); }
|
||||
|
||||
template <typename... Args>
|
||||
T& emplace_back(Args&&... args)
|
||||
SmallVector(const SmallVector& other)
|
||||
{
|
||||
return m_array[m_size++] = T{std::forward<Args>(args)...};
|
||||
for (auto& value : other)
|
||||
emplace_back(value);
|
||||
}
|
||||
|
||||
T& operator[](size_t i) { return m_array[i]; }
|
||||
const T& operator[](size_t i) const { return m_array[i]; }
|
||||
SmallVector& operator=(const SmallVector& rhs)
|
||||
{
|
||||
clear();
|
||||
for (auto& value : rhs)
|
||||
emplace_back(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto data() { return m_array.data(); }
|
||||
auto begin() { return m_array.begin(); }
|
||||
auto end() { return m_array.begin() + m_size; }
|
||||
SmallVector(SmallVector&& other)
|
||||
{
|
||||
for (auto& value : other)
|
||||
emplace_back(std::move(value));
|
||||
other.clear();
|
||||
}
|
||||
|
||||
auto data() const { return m_array.data(); }
|
||||
auto begin() const { return m_array.begin(); }
|
||||
auto end() const { return m_array.begin() + m_size; }
|
||||
SmallVector& operator=(SmallVector&& rhs)
|
||||
{
|
||||
clear();
|
||||
for (auto& value : rhs)
|
||||
emplace_back(std::move(value));
|
||||
rhs.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void push_back(const value_type& x) { emplace_back(x); }
|
||||
void push_back(value_type&& x) { emplace_back(std::move(x)); }
|
||||
|
||||
template <typename... Args>
|
||||
value_type& emplace_back(Args&&... args)
|
||||
{
|
||||
assert(m_size < MaxSize);
|
||||
return *::new (&m_array[m_size++ * sizeof(value_type)]) value_type{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
assert(m_size > 0);
|
||||
std::destroy_at(data() + --m_size);
|
||||
}
|
||||
|
||||
value_type& operator[](size_t i)
|
||||
{
|
||||
assert(i < m_size);
|
||||
return data()[i];
|
||||
}
|
||||
const value_type& operator[](size_t i) const
|
||||
{
|
||||
assert(i < m_size);
|
||||
return data()[i];
|
||||
}
|
||||
|
||||
auto data() { return std::launder(reinterpret_cast<value_type*>(m_array.data())); }
|
||||
auto begin() { return data(); }
|
||||
auto end() { return data() + m_size; }
|
||||
|
||||
auto data() const { return std::launder(reinterpret_cast<const value_type*>(m_array.data())); }
|
||||
auto begin() const { return data(); }
|
||||
auto end() const { return data() + m_size; }
|
||||
|
||||
size_t capacity() const { return MaxSize; }
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
|
||||
void clear() { m_size = 0; }
|
||||
void resize(size_t new_size)
|
||||
{
|
||||
assert(new_size <= MaxSize);
|
||||
|
||||
while (size() < new_size)
|
||||
emplace_back();
|
||||
|
||||
while (size() > new_size)
|
||||
pop_back();
|
||||
}
|
||||
|
||||
void clear() { resize(0); }
|
||||
|
||||
private:
|
||||
std::array<T, MaxSize> m_array{};
|
||||
alignas(value_type) std::array<std::byte, MaxSize * sizeof(value_type)> m_array;
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -183,10 +183,7 @@ void LoadPatches()
|
|||
LoadPatchSection("OnFrame", &s_on_frame, globalIni, localIni);
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID());
|
||||
}
|
||||
AchievementManager::GetInstance().FilterApprovedPatches(s_on_frame, sconfig.GetGameID());
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// Check if I'm syncing Codes
|
||||
|
|
|
@ -156,7 +156,7 @@ void JitArm64::FixGTBeforeSettingCRFieldBit(ARM64Reg reg)
|
|||
// doesn't accidentally become considered set. Gross but necessary; this can break actual games.
|
||||
auto WA = gpr.GetScopedReg();
|
||||
ARM64Reg XA = EncodeRegTo64(WA);
|
||||
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
|
||||
MOVI2R(XA, 1ULL << 63);
|
||||
CMP(reg, ARM64Reg::ZR);
|
||||
CSEL(reg, reg, XA, CC_NEQ);
|
||||
}
|
||||
|
|
|
@ -161,6 +161,8 @@ void ControlExpressionSyntaxHighlighter::highlightBlock(const QString&)
|
|||
case TokenType::TOK_LPAREN:
|
||||
case TokenType::TOK_RPAREN:
|
||||
case TokenType::TOK_COMMA:
|
||||
case TokenType::TOK_QUESTION:
|
||||
case TokenType::TOK_COLON:
|
||||
char_format = GetSpecialCharFormat();
|
||||
break;
|
||||
case TokenType::TOK_LITERAL:
|
||||
|
@ -304,6 +306,7 @@ void IOWindow::CreateMainLayout()
|
|||
m_operators_combo->addItem(tr("< Less-than"));
|
||||
m_operators_combo->addItem(tr("& And"));
|
||||
m_operators_combo->addItem(tr("^ Xor"));
|
||||
m_operators_combo->addItem(tr("? Conditional"));
|
||||
}
|
||||
m_operators_combo->addItem(tr("| Or"));
|
||||
m_operators_combo->addItem(tr("$ User Variable"));
|
||||
|
|
|
@ -579,3 +579,8 @@ void MappingWindow::ShowExtensionMotionTabs(bool show)
|
|||
m_tab_widget->removeTab(4);
|
||||
}
|
||||
}
|
||||
|
||||
void MappingWindow::ActivateExtensionTab()
|
||||
{
|
||||
m_tab_widget->setCurrentIndex(3);
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
ControllerEmu::EmulatedController* GetController() const;
|
||||
bool IsMappingAllDevices() const;
|
||||
void ShowExtensionMotionTabs(bool show);
|
||||
void ActivateExtensionTab();
|
||||
|
||||
signals:
|
||||
// Emitted when config has changed so widgets can update to reflect the change.
|
||||
|
|
|
@ -59,7 +59,13 @@ void WiimoteEmuGeneral::CreateMainLayout()
|
|||
|
||||
extension->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
|
||||
static_cast<QFormLayout*>(extension->layout())->insertRow(0, combo_hbox);
|
||||
auto* const ext_layout = static_cast<QFormLayout*>(extension->layout());
|
||||
ext_layout->insertRow(0, combo_hbox);
|
||||
|
||||
m_configure_ext_button = new QPushButton(tr("Configure Extension"));
|
||||
m_configure_ext_button->setDisabled(true);
|
||||
m_configure_ext_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
ext_layout->insertRow(1, m_configure_ext_button);
|
||||
|
||||
layout->addWidget(extension, 0, 3);
|
||||
layout->addWidget(CreateGroupBox(tr("Rumble"), Wiimote::GetWiimoteGroup(
|
||||
|
@ -81,6 +87,8 @@ void WiimoteEmuGeneral::Connect()
|
|||
connect(m_extension_combo, &QComboBox::activated, this, &WiimoteEmuGeneral::OnAttachmentSelected);
|
||||
connect(this, &MappingWidget::ConfigChanged, this, &WiimoteEmuGeneral::ConfigChanged);
|
||||
connect(this, &MappingWidget::Update, this, &WiimoteEmuGeneral::Update);
|
||||
connect(m_configure_ext_button, &QPushButton::clicked, GetParent(),
|
||||
&MappingWindow::ActivateExtensionTab);
|
||||
}
|
||||
|
||||
void WiimoteEmuGeneral::OnAttachmentChanged(int extension)
|
||||
|
@ -88,6 +96,8 @@ void WiimoteEmuGeneral::OnAttachmentChanged(int extension)
|
|||
GetParent()->ShowExtensionMotionTabs(extension == WiimoteEmu::ExtensionNumber::NUNCHUK);
|
||||
|
||||
m_extension_widget->ChangeExtensionType(extension);
|
||||
|
||||
m_configure_ext_button->setEnabled(extension != WiimoteEmu::ExtensionNumber::NONE);
|
||||
}
|
||||
|
||||
void WiimoteEmuGeneral::OnAttachmentSelected(int extension)
|
||||
|
|
|
@ -34,6 +34,7 @@ private:
|
|||
// Extensions
|
||||
QComboBox* m_extension_combo;
|
||||
QLabel* m_extension_combo_dynamic_indicator;
|
||||
QPushButton* m_configure_ext_button;
|
||||
|
||||
WiimoteEmuExtension* m_extension_widget;
|
||||
};
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QColorDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QSignalBlocker>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableWidget>
|
||||
#include <QtGlobal>
|
||||
|
||||
|
@ -42,6 +44,7 @@ constexpr int MISC_COLUMNS = 2;
|
|||
constexpr auto USER_ROLE_IS_ROW_BREAKPOINT_CELL = Qt::UserRole;
|
||||
constexpr auto USER_ROLE_CELL_ADDRESS = Qt::UserRole + 1;
|
||||
constexpr auto USER_ROLE_VALUE_TYPE = Qt::UserRole + 2;
|
||||
constexpr auto USER_ROLE_VALID_ADDRESS = Qt::UserRole + 3;
|
||||
|
||||
// Numbers for the scrollbar. These affect how much big the draggable part of the scrollbar is, how
|
||||
// smooth it scrolls, and how much memory it traverses while dragging.
|
||||
|
@ -52,6 +55,14 @@ constexpr int SCROLLBAR_CENTER = SCROLLBAR_MAXIMUM / 2;
|
|||
|
||||
const QString INVALID_MEMORY = QStringLiteral("-");
|
||||
|
||||
void TableEditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,
|
||||
const QModelIndex& index) const
|
||||
{
|
||||
// Triggers on placing data into a cell. Editor has the text to be input, index has the location.
|
||||
const QString input = qobject_cast<QLineEdit*>(editor)->text();
|
||||
emit editFinished(index.row(), index.column(), input);
|
||||
}
|
||||
|
||||
class MemoryViewTable final : public QTableWidget
|
||||
{
|
||||
public:
|
||||
|
@ -66,6 +77,11 @@ public:
|
|||
setSelectionMode(NoSelection);
|
||||
setTextElideMode(Qt::TextElideMode::ElideNone);
|
||||
|
||||
// Route user's direct cell inputs to an editFinished signal. Much better than an itemChanged
|
||||
// signal and QSignalBlock juggling.
|
||||
TableEditDelegate* table_edit_delegate = new TableEditDelegate(this);
|
||||
setItemDelegate(table_edit_delegate);
|
||||
|
||||
// Prevent colors from changing based on focus.
|
||||
QPalette palette(m_view->palette());
|
||||
palette.setBrush(QPalette::Inactive, QPalette::Highlight, palette.brush(QPalette::Highlight));
|
||||
|
@ -78,13 +94,17 @@ public:
|
|||
|
||||
connect(this, &MemoryViewTable::customContextMenuRequested, m_view,
|
||||
&MemoryViewWidget::OnContextMenu);
|
||||
connect(this, &MemoryViewTable::itemChanged, this, &MemoryViewTable::OnItemChanged);
|
||||
connect(table_edit_delegate, &TableEditDelegate::editFinished, this,
|
||||
&MemoryViewTable::OnDirectTableEdit);
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent* event) override
|
||||
{
|
||||
QTableWidget::resizeEvent(event);
|
||||
m_view->CreateTable();
|
||||
// Remakes table if vertically resized
|
||||
const int rows = std::round((height() / static_cast<float>(rowHeight(0))) - 0.25);
|
||||
if (rows != rowCount())
|
||||
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Full);
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override
|
||||
|
@ -93,24 +113,21 @@ public:
|
|||
{
|
||||
case Qt::Key_Up:
|
||||
m_view->m_address -= m_view->m_bytes_per_row;
|
||||
m_view->Update();
|
||||
return;
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
m_view->m_address += m_view->m_bytes_per_row;
|
||||
m_view->Update();
|
||||
return;
|
||||
break;
|
||||
case Qt::Key_PageUp:
|
||||
m_view->m_address -= this->rowCount() * m_view->m_bytes_per_row;
|
||||
m_view->Update();
|
||||
return;
|
||||
break;
|
||||
case Qt::Key_PageDown:
|
||||
m_view->m_address += this->rowCount() * m_view->m_bytes_per_row;
|
||||
m_view->Update();
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
QWidget::keyPressEvent(event);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Addresses);
|
||||
}
|
||||
|
||||
void wheelEvent(QWheelEvent* event) override
|
||||
|
@ -122,7 +139,7 @@ public:
|
|||
return;
|
||||
|
||||
m_view->m_address += delta * m_view->m_bytes_per_row;
|
||||
m_view->Update();
|
||||
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Addresses);
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override
|
||||
|
@ -138,7 +155,6 @@ public:
|
|||
{
|
||||
const u32 address = item->data(USER_ROLE_CELL_ADDRESS).toUInt();
|
||||
m_view->ToggleBreakpoint(address, true);
|
||||
m_view->Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -146,9 +162,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void OnItemChanged(QTableWidgetItem* item)
|
||||
void OnDirectTableEdit(const int row, const int column, const QString& text)
|
||||
{
|
||||
QString text = item->text();
|
||||
QTableWidgetItem* item = this->item(row, column);
|
||||
MemoryViewWidget::Type type =
|
||||
static_cast<MemoryViewWidget::Type>(item->data(USER_ROLE_VALUE_TYPE).toInt());
|
||||
std::vector<u8> bytes = m_view->ConvertTextToBytes(type, text);
|
||||
|
@ -156,17 +172,18 @@ public:
|
|||
u32 address = item->data(USER_ROLE_CELL_ADDRESS).toUInt();
|
||||
u32 end_address = address + static_cast<u32>(bytes.size()) - 1;
|
||||
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_view->GetAddressSpace());
|
||||
|
||||
const Core::CPUThreadGuard guard(m_view->m_system);
|
||||
|
||||
if (!bytes.empty() && accessors->IsValidAddress(guard, address) &&
|
||||
accessors->IsValidAddress(guard, end_address))
|
||||
{
|
||||
for (const u8 c : bytes)
|
||||
accessors->WriteU8(guard, address++, c);
|
||||
const Core::CPUThreadGuard guard(m_view->m_system);
|
||||
|
||||
if (!bytes.empty() && accessors->IsValidAddress(guard, address) &&
|
||||
accessors->IsValidAddress(guard, end_address))
|
||||
{
|
||||
for (const u8 c : bytes)
|
||||
accessors->WriteU8(guard, address++, c);
|
||||
}
|
||||
}
|
||||
|
||||
m_view->Update();
|
||||
m_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Values);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -198,13 +215,25 @@ MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
|
|||
this->setLayout(layout);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont);
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
qOverload<>(&MemoryViewWidget::UpdateColumns));
|
||||
connect(Host::GetInstance(), &Host::PPCBreakpointsChanged, this,
|
||||
qOverload<>(&MemoryViewWidget::Update));
|
||||
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
|
||||
qOverload<>(&MemoryViewWidget::UpdateColumns));
|
||||
connect(&Settings::Instance(), &Settings::ThemeChanged, this, &MemoryViewWidget::Update);
|
||||
&MemoryViewWidget::UpdateBreakpointTags);
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
|
||||
// UpdateDisasmDialog currently catches pauses, no need to signal it twice.
|
||||
if (Core::GetState(m_system) != Core::State::Paused)
|
||||
UpdateDispatcher(UpdateType::Values);
|
||||
});
|
||||
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, [this] {
|
||||
// Disasm spam will break updates while running. Only need it for things like steps when paused
|
||||
// and breaks which trigger a pause.
|
||||
if (Core::GetState(m_system) != Core::State::Running)
|
||||
UpdateDispatcher(UpdateType::Values);
|
||||
});
|
||||
|
||||
// CPU Thread to Main Thread.
|
||||
connect(this, &MemoryViewWidget::AutoUpdate, this,
|
||||
[this] { UpdateDispatcher(UpdateType::Auto); });
|
||||
connect(&Settings::Instance(), &Settings::ThemeChanged, this,
|
||||
[this] { UpdateDispatcher(UpdateType::Full); });
|
||||
|
||||
// Also calls create table.
|
||||
UpdateFont(Settings::Instance().GetDebugFont());
|
||||
|
@ -220,7 +249,7 @@ void MemoryViewWidget::UpdateFont(const QFont& font)
|
|||
m_font_width = fm.horizontalAdvance(QLatin1Char('0'));
|
||||
m_table->setFont(font);
|
||||
|
||||
CreateTable();
|
||||
UpdateDispatcher(UpdateType::Full);
|
||||
}
|
||||
|
||||
constexpr int GetTypeSize(MemoryViewWidget::Type type)
|
||||
|
@ -282,6 +311,42 @@ constexpr int GetCharacterCount(MemoryViewWidget::Type type)
|
|||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::UpdateDispatcher(UpdateType type)
|
||||
{
|
||||
std::unique_lock lock(m_updating, std::defer_lock);
|
||||
|
||||
// A full update may change parameters like row count, so make sure it goes through.
|
||||
if (type == UpdateType::Full)
|
||||
lock.lock();
|
||||
else if (!isVisible() || !lock.try_lock())
|
||||
return;
|
||||
|
||||
// Check if table needs to be created.
|
||||
if (m_table->item(2, 1) == nullptr)
|
||||
type = UpdateType::Full;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UpdateType::Full:
|
||||
CreateTable();
|
||||
[[fallthrough]];
|
||||
case UpdateType::Addresses:
|
||||
Update();
|
||||
[[fallthrough]];
|
||||
case UpdateType::Values:
|
||||
if (Core::GetState(m_system) == Core::State::Paused)
|
||||
GetValues();
|
||||
UpdateColumns();
|
||||
break;
|
||||
case UpdateType::Auto:
|
||||
// Values were captured on CPU thread while doing a callback.
|
||||
if (m_values.size() != 0)
|
||||
UpdateColumns();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::CreateTable()
|
||||
{
|
||||
m_table->clearContents();
|
||||
|
@ -292,8 +357,6 @@ void MemoryViewWidget::CreateTable()
|
|||
m_table->verticalHeader()->setMinimumSectionSize(m_font_vspace);
|
||||
m_table->horizontalHeader()->setMinimumSectionSize(m_font_width * 2);
|
||||
|
||||
const QSignalBlocker blocker(m_table);
|
||||
|
||||
// Set column and row parameters.
|
||||
// Span is the number of unique memory values covered in one row.
|
||||
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
|
||||
|
@ -388,17 +451,10 @@ void MemoryViewWidget::CreateTable()
|
|||
const int width = m_font_width * GetCharacterCount(m_type);
|
||||
for (int i = start_fill; i < total_columns; i++)
|
||||
m_table->setColumnWidth(i, width);
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void MemoryViewWidget::Update()
|
||||
{
|
||||
// Check if table is created
|
||||
if (m_table->item(1, 1) == nullptr)
|
||||
return;
|
||||
|
||||
const QSignalBlocker blocker(m_table);
|
||||
m_table->clearSelection();
|
||||
|
||||
// Update addresses
|
||||
|
@ -406,6 +462,9 @@ void MemoryViewWidget::Update()
|
|||
u32 row_address = address - (m_table->rowCount() / 2) * m_bytes_per_row;
|
||||
const int data_span = m_bytes_per_row / GetTypeSize(m_type);
|
||||
|
||||
m_address_range.first = row_address;
|
||||
m_address_range.second = row_address + m_table->rowCount() * m_bytes_per_row - 1;
|
||||
|
||||
for (int i = 0; i < m_table->rowCount(); i++, row_address += m_bytes_per_row)
|
||||
{
|
||||
auto* bp_item = m_table->item(i, 0);
|
||||
|
@ -426,69 +485,139 @@ void MemoryViewWidget::Update()
|
|||
item_address = row_address + c * GetTypeSize(m_type);
|
||||
|
||||
item->setData(USER_ROLE_CELL_ADDRESS, item_address);
|
||||
|
||||
// Reset highlighting.
|
||||
item->setBackground(Qt::transparent);
|
||||
item->setData(USER_ROLE_VALID_ADDRESS, false);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateColumns();
|
||||
UpdateBreakpointTags();
|
||||
|
||||
m_table->viewport()->update();
|
||||
m_table->update();
|
||||
update();
|
||||
}
|
||||
|
||||
void MemoryViewWidget::UpdateColumns()
|
||||
{
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
// Check if table is created
|
||||
if (m_table->item(1, 1) == nullptr)
|
||||
return;
|
||||
|
||||
if (Core::GetState(m_system) == Core::State::Paused)
|
||||
{
|
||||
const Core::CPUThreadGuard guard(m_system);
|
||||
UpdateColumns(&guard);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the core is running, blank out the view of memory instead of reading anything.
|
||||
UpdateColumns(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::UpdateColumns(const Core::CPUThreadGuard* guard)
|
||||
{
|
||||
// Check if table is created
|
||||
if (m_table->item(1, 1) == nullptr)
|
||||
return;
|
||||
|
||||
const QSignalBlocker blocker(m_table);
|
||||
|
||||
for (int i = 0; i < m_table->rowCount(); i++)
|
||||
{
|
||||
for (int c = 0; c < m_data_columns; c++)
|
||||
{
|
||||
auto* cell_item = m_table->item(i, c + MISC_COLUMNS);
|
||||
if (!cell_item)
|
||||
return;
|
||||
|
||||
const u32 cell_address = cell_item->data(USER_ROLE_CELL_ADDRESS).toUInt();
|
||||
const Type type = static_cast<Type>(cell_item->data(USER_ROLE_VALUE_TYPE).toInt());
|
||||
std::optional<QString> new_text;
|
||||
|
||||
cell_item->setText(guard ? ValueToString(*guard, cell_address, type) : INVALID_MEMORY);
|
||||
// Dual view auto sets the type of the left-side based on m_type. Only time type and
|
||||
// m_type differ.
|
||||
if (type != m_type)
|
||||
{
|
||||
new_text = m_values_dual_view.empty() || !m_values_dual_view.contains(cell_address) ?
|
||||
std::nullopt :
|
||||
m_values_dual_view.at(cell_address);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_text = m_values.empty() || !m_values.contains(cell_address) ? std::nullopt :
|
||||
m_values.at(cell_address);
|
||||
}
|
||||
|
||||
// Set search address to selected / colored
|
||||
if (cell_address == m_address_highlight)
|
||||
cell_item->setSelected(true);
|
||||
|
||||
// Color recently changed items.
|
||||
QColor bcolor = cell_item->background().color();
|
||||
const bool valid = cell_item->data(USER_ROLE_VALID_ADDRESS).toBool();
|
||||
|
||||
// It gets a bit complicated, because invalid addresses becoming valid should not be
|
||||
// colored.
|
||||
if (!new_text.has_value())
|
||||
{
|
||||
cell_item->setBackground(Qt::transparent);
|
||||
cell_item->setData(USER_ROLE_VALID_ADDRESS, false);
|
||||
cell_item->setText(INVALID_MEMORY);
|
||||
}
|
||||
else if (!valid)
|
||||
{
|
||||
// Wasn't valid on last update, is valid now.
|
||||
cell_item->setData(USER_ROLE_VALID_ADDRESS, true);
|
||||
cell_item->setText(new_text.value());
|
||||
}
|
||||
else if (bcolor.rgb() != m_highlight_color.rgb() && bcolor != Qt::transparent)
|
||||
{
|
||||
// Filter out colors that shouldn't change, such as breakpoints.
|
||||
cell_item->setText(new_text.value());
|
||||
}
|
||||
else if (cell_item->text() != new_text.value())
|
||||
{
|
||||
// Cell changed, apply highlighting.
|
||||
cell_item->setBackground(m_highlight_color);
|
||||
cell_item->setText(new_text.value());
|
||||
}
|
||||
else if (bcolor.alpha() > 0)
|
||||
{
|
||||
// Fade out highlighting each frame.
|
||||
bcolor.setAlpha(bcolor.alpha() - 1);
|
||||
cell_item->setBackground(bcolor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always runs on CPU thread from a callback.
|
||||
void MemoryViewWidget::UpdateOnFrameEnd()
|
||||
{
|
||||
std::unique_lock lock(m_updating, std::try_to_lock);
|
||||
if (lock)
|
||||
{
|
||||
GetValues();
|
||||
// Should not directly trigger widget updates on a cpu thread. Signal main thread to do it.
|
||||
emit AutoUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::GetValues()
|
||||
{
|
||||
m_values.clear();
|
||||
m_values_dual_view.clear();
|
||||
|
||||
// Check for dual view type
|
||||
Type type = Type::Null;
|
||||
|
||||
if (m_dual_view)
|
||||
{
|
||||
if (GetTypeSize(m_type) == 1)
|
||||
type = Type::Hex8;
|
||||
else if (GetTypeSize(m_type) == 2)
|
||||
type = Type::Hex16;
|
||||
else if (GetTypeSize(m_type) == 8)
|
||||
type = Type::Hex64;
|
||||
else
|
||||
type = Type::Hex32;
|
||||
}
|
||||
|
||||
// Grab memory values as QStrings
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
|
||||
for (u32 address = m_address_range.first; address <= m_address_range.second;
|
||||
address += GetTypeSize(m_type))
|
||||
{
|
||||
m_values.insert(std::pair(address, ValueToString(guard, address, m_type)));
|
||||
|
||||
if (m_dual_view)
|
||||
m_values_dual_view.insert(std::pair(address, ValueToString(guard, address, type)));
|
||||
}
|
||||
}
|
||||
|
||||
// May only be called if we have taken on the role of the CPU thread
|
||||
QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type)
|
||||
std::optional<QString> MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard,
|
||||
u32 address, Type type)
|
||||
{
|
||||
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
|
||||
if (!accessors->IsValidAddress(guard, address))
|
||||
return INVALID_MEMORY;
|
||||
return std::nullopt;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
@ -550,7 +679,7 @@ QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 a
|
|||
return string;
|
||||
}
|
||||
default:
|
||||
return INVALID_MEMORY;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -606,7 +735,7 @@ void MemoryViewWidget::SetAddressSpace(AddressSpace::Type address_space)
|
|||
}
|
||||
|
||||
m_address_space = address_space;
|
||||
Update();
|
||||
UpdateDispatcher(UpdateType::Addresses);
|
||||
}
|
||||
|
||||
AddressSpace::Type MemoryViewWidget::GetAddressSpace() const
|
||||
|
@ -777,7 +906,55 @@ void MemoryViewWidget::SetDisplay(Type type, int bytes_per_row, int alignment, b
|
|||
else
|
||||
m_alignment = alignment;
|
||||
|
||||
CreateTable();
|
||||
UpdateDispatcher(UpdateType::Full);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::ToggleHighlights(bool enabled)
|
||||
{
|
||||
// m_highlight_color should hold the current highlight color even when disabled, so it can
|
||||
// be used if re-enabled. If modifying the enabled alpha, change in .h file as well.
|
||||
if (enabled)
|
||||
{
|
||||
m_highlight_color.setAlpha(100);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treated as being interchangable with Qt::transparent.
|
||||
m_highlight_color.setAlpha(0);
|
||||
|
||||
// Immediately remove highlights when paused.
|
||||
for (int i = 0; i < m_table->rowCount(); i++)
|
||||
{
|
||||
for (int c = 0; c < m_data_columns; c++)
|
||||
m_table->item(i, c + MISC_COLUMNS)->setBackground(m_highlight_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::SetHighlightColor()
|
||||
{
|
||||
// Could allow custom alphas to be set, which would change fade-out rate.
|
||||
QColor color = QColorDialog::getColor(m_highlight_color);
|
||||
if (!color.isValid())
|
||||
return;
|
||||
|
||||
const bool enabled = m_highlight_color.alpha() != 0;
|
||||
m_highlight_color = color;
|
||||
m_highlight_color.setAlpha(enabled ? 100 : 0);
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
// Immediately update colors. Only useful for playing with colors while paused.
|
||||
for (int i = 0; i < m_table->rowCount(); i++)
|
||||
{
|
||||
for (int c = 0; c < m_data_columns; c++)
|
||||
{
|
||||
auto* item = m_table->item(i, c + MISC_COLUMNS);
|
||||
// Get current cell alpha state.
|
||||
color.setAlpha(item->background().color().alpha());
|
||||
m_table->item(i, c + MISC_COLUMNS)->setBackground(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::SetBPType(BPType type)
|
||||
|
@ -793,7 +970,7 @@ void MemoryViewWidget::SetAddress(u32 address)
|
|||
|
||||
m_address = address;
|
||||
|
||||
Update();
|
||||
UpdateDispatcher(UpdateType::Addresses);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::SetBPLoggingEnabled(bool enabled)
|
||||
|
@ -916,7 +1093,7 @@ void MemoryViewWidget::ScrollbarActionTriggered(int action)
|
|||
// User is currently dragging the scrollbar.
|
||||
// Adjust the memory view by the exact drag difference.
|
||||
m_address += difference * m_bytes_per_row;
|
||||
Update();
|
||||
UpdateDispatcher(UpdateType::Addresses);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -931,7 +1108,7 @@ void MemoryViewWidget::ScrollbarActionTriggered(int action)
|
|||
m_address += (difference < 0 ? -1 : 1) * m_bytes_per_row * m_table->rowCount();
|
||||
}
|
||||
|
||||
Update();
|
||||
UpdateDispatcher(UpdateType::Addresses);
|
||||
// Manually reset the draggable part of the bar back to the center.
|
||||
m_scrollbar->setSliderPosition(SCROLLBAR_CENTER);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <mutex>
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -22,6 +24,21 @@ class CPUThreadGuard;
|
|||
class System;
|
||||
} // namespace Core
|
||||
|
||||
// Captures direct editing of the table.
|
||||
class TableEditDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TableEditDelegate(QObject* parent) : QStyledItemDelegate(parent) {}
|
||||
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model,
|
||||
const QModelIndex& index) const override;
|
||||
|
||||
signals:
|
||||
void editFinished(const int row, const int column, const QString& text) const;
|
||||
};
|
||||
|
||||
class MemoryViewTable;
|
||||
|
||||
class MemoryViewWidget final : public QWidget
|
||||
|
@ -54,10 +71,21 @@ public:
|
|||
WriteOnly
|
||||
};
|
||||
|
||||
enum class UpdateType
|
||||
{
|
||||
Full,
|
||||
Addresses,
|
||||
Values,
|
||||
Auto,
|
||||
};
|
||||
|
||||
explicit MemoryViewWidget(Core::System& system, QWidget* parent = nullptr);
|
||||
|
||||
void CreateTable();
|
||||
void UpdateDispatcher(UpdateType type = UpdateType::Addresses);
|
||||
void Update();
|
||||
void UpdateOnFrameEnd();
|
||||
void GetValues();
|
||||
void UpdateFont(const QFont& font);
|
||||
void ToggleBreakpoint(u32 addr, bool row);
|
||||
|
||||
|
@ -65,6 +93,8 @@ public:
|
|||
void SetAddressSpace(AddressSpace::Type address_space);
|
||||
AddressSpace::Type GetAddressSpace() const;
|
||||
void SetDisplay(Type type, int bytes_per_row, int alignment, bool dual_view);
|
||||
void ToggleHighlights(bool enabled);
|
||||
void SetHighlightColor();
|
||||
void SetBPType(BPType type);
|
||||
void SetAddress(u32 address);
|
||||
void SetFocus() const;
|
||||
|
@ -72,6 +102,7 @@ public:
|
|||
void SetBPLoggingEnabled(bool enabled);
|
||||
|
||||
signals:
|
||||
void AutoUpdate();
|
||||
void ShowCode(u32 address);
|
||||
void RequestWatch(QString name, u32 address);
|
||||
|
||||
|
@ -81,10 +112,9 @@ private:
|
|||
void OnCopyHex(u32 addr);
|
||||
void UpdateBreakpointTags();
|
||||
void UpdateColumns();
|
||||
void UpdateColumns(const Core::CPUThreadGuard* guard);
|
||||
void ScrollbarActionTriggered(int action);
|
||||
void ScrollbarSliderReleased();
|
||||
QString ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
|
||||
std::optional<QString> ValueToString(const Core::CPUThreadGuard& guard, u32 address, Type type);
|
||||
|
||||
Core::System& m_system;
|
||||
|
||||
|
@ -95,6 +125,9 @@ private:
|
|||
BPType m_bp_type = BPType::ReadWrite;
|
||||
bool m_do_log = true;
|
||||
u32 m_address = 0x80000000;
|
||||
std::pair<u32, u32> m_address_range;
|
||||
std::map<u32, std::optional<QString>> m_values;
|
||||
std::map<u32, std::optional<QString>> m_values_dual_view;
|
||||
u32 m_address_highlight = 0;
|
||||
int m_font_width = 0;
|
||||
int m_font_vspace = 0;
|
||||
|
@ -102,6 +135,8 @@ private:
|
|||
int m_alignment = 16;
|
||||
int m_data_columns;
|
||||
bool m_dual_view = false;
|
||||
std::mutex m_updating;
|
||||
QColor m_highlight_color = QColor(120, 255, 255, 100);
|
||||
|
||||
friend class MemoryViewTable;
|
||||
};
|
||||
|
|
|
@ -66,9 +66,13 @@ MemoryWidget::MemoryWidget(Core::System& system, QWidget* parent)
|
|||
connect(&Settings::Instance(), &Settings::DebugModeToggled, this,
|
||||
[this](bool enabled) { setHidden(!enabled || !Settings::Instance().IsMemoryVisible()); });
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, &MemoryWidget::Update);
|
||||
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &MemoryWidget::Update);
|
||||
|
||||
connect(this, &QDockWidget::visibilityChanged, this, [this](bool visible) {
|
||||
// Stop auto-update if MemoryView is tabbed out.
|
||||
if (visible && m_auto_update_enabled)
|
||||
RegisterAfterFrameEventCallback();
|
||||
else
|
||||
RemoveAfterFrameEventCallback();
|
||||
});
|
||||
LoadSettings();
|
||||
|
||||
ConnectWidgets();
|
||||
|
@ -250,12 +254,34 @@ void MemoryWidget::CreateWidgets()
|
|||
// Sidebar top menu
|
||||
QMenuBar* menubar = new QMenuBar(sidebar);
|
||||
menubar->setNativeMenuBar(false);
|
||||
QMenu* menu_views = new QMenu(tr("&View"), menubar);
|
||||
menubar->addMenu(menu_views);
|
||||
|
||||
QMenu* menu_import = new QMenu(tr("&Import"), menubar);
|
||||
menu_import->addAction(tr("&Load file to current address"), this,
|
||||
&MemoryWidget::OnSetValueFromFile);
|
||||
menubar->addMenu(menu_import);
|
||||
|
||||
auto* auto_update_action =
|
||||
menu_views->addAction(tr("Auto update memory values"), this, [this](bool checked) {
|
||||
m_auto_update_enabled = checked;
|
||||
if (checked)
|
||||
RegisterAfterFrameEventCallback();
|
||||
else
|
||||
RemoveAfterFrameEventCallback();
|
||||
});
|
||||
auto_update_action->setCheckable(true);
|
||||
auto_update_action->setChecked(true);
|
||||
|
||||
auto* highlight_update_action =
|
||||
menu_views->addAction(tr("Highlight recently changed values"), this,
|
||||
[this](bool checked) { m_memory_view->ToggleHighlights(checked); });
|
||||
highlight_update_action->setCheckable(true);
|
||||
highlight_update_action->setChecked(true);
|
||||
|
||||
menu_views->addAction(tr("Highlight color"), this,
|
||||
[this] { m_memory_view->SetHighlightColor(); });
|
||||
|
||||
QMenu* menu_export = new QMenu(tr("&Export"), menubar);
|
||||
menu_export->addAction(tr("Dump &MRAM"), this, &MemoryWidget::OnDumpMRAM);
|
||||
menu_export->addAction(tr("Dump &ExRAM"), this, &MemoryWidget::OnDumpExRAM);
|
||||
|
@ -340,19 +366,43 @@ void MemoryWidget::ConnectWidgets()
|
|||
void MemoryWidget::closeEvent(QCloseEvent*)
|
||||
{
|
||||
Settings::Instance().SetMemoryVisible(false);
|
||||
RemoveAfterFrameEventCallback();
|
||||
}
|
||||
|
||||
void MemoryWidget::showEvent(QShowEvent* event)
|
||||
{
|
||||
if (m_auto_update_enabled)
|
||||
RegisterAfterFrameEventCallback();
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void MemoryWidget::hideEvent(QHideEvent* event)
|
||||
{
|
||||
RemoveAfterFrameEventCallback();
|
||||
}
|
||||
|
||||
void MemoryWidget::RegisterAfterFrameEventCallback()
|
||||
{
|
||||
m_vi_end_field_event = VIEndFieldEvent::Register([this] { AutoUpdateTable(); }, "MemoryWidget");
|
||||
}
|
||||
|
||||
void MemoryWidget::RemoveAfterFrameEventCallback()
|
||||
{
|
||||
m_vi_end_field_event.reset();
|
||||
}
|
||||
|
||||
void MemoryWidget::AutoUpdateTable()
|
||||
{
|
||||
m_memory_view->UpdateOnFrameEnd();
|
||||
}
|
||||
|
||||
void MemoryWidget::Update()
|
||||
{
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
m_memory_view->Update();
|
||||
m_memory_view->UpdateDispatcher(MemoryViewWidget::UpdateType::Addresses);
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QDockWidget>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
|
||||
class MemoryViewWidget;
|
||||
class QCheckBox;
|
||||
|
@ -76,7 +77,11 @@ private:
|
|||
void FindValue(bool next);
|
||||
|
||||
void closeEvent(QCloseEvent*) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void RegisterAfterFrameEventCallback();
|
||||
void RemoveAfterFrameEventCallback();
|
||||
void AutoUpdateTable();
|
||||
|
||||
Core::System& m_system;
|
||||
|
||||
|
@ -109,4 +114,7 @@ private:
|
|||
QRadioButton* m_bp_read_only;
|
||||
QRadioButton* m_bp_write_only;
|
||||
QCheckBox* m_bp_log_check;
|
||||
Common::EventHook m_vi_end_field_event;
|
||||
|
||||
bool m_auto_update_enabled = true;
|
||||
};
|
||||
|
|
|
@ -171,6 +171,10 @@ Token Lexer::NextToken()
|
|||
return Token(TOK_RPAREN);
|
||||
case '@':
|
||||
return Token(TOK_HOTKEY);
|
||||
case '?':
|
||||
return Token(TOK_QUESTION);
|
||||
case ':':
|
||||
return Token(TOK_COLON);
|
||||
case '&':
|
||||
return Token(TOK_AND);
|
||||
case '|':
|
||||
|
@ -747,7 +751,7 @@ private:
|
|||
{
|
||||
// Read one argument.
|
||||
// Grab an expression, but stop at comma.
|
||||
auto arg = ParseBinary(BinaryOperatorPrecedence(TOK_COMMA));
|
||||
auto arg = ParseInfixOperations(OperatorPrecedence(TOK_COMMA));
|
||||
if (ParseStatus::Successful != arg.status)
|
||||
return arg;
|
||||
|
||||
|
@ -846,7 +850,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
static int BinaryOperatorPrecedence(TokenType type)
|
||||
static constexpr int OperatorPrecedence(TokenType type = TOK_EOF)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -867,16 +871,16 @@ private:
|
|||
case TOK_OR:
|
||||
return 6;
|
||||
case TOK_ASSIGN:
|
||||
case TOK_QUESTION:
|
||||
return 7;
|
||||
case TOK_COMMA:
|
||||
return 8;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
return 999;
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult ParseBinary(int precedence = 999)
|
||||
ParseResult ParseInfixOperations(int precedence = OperatorPrecedence())
|
||||
{
|
||||
ParseResult lhs = ParseAtom(Chew());
|
||||
|
||||
|
@ -886,18 +890,48 @@ private:
|
|||
std::unique_ptr<Expression> expr = std::move(lhs.expr);
|
||||
|
||||
// TODO: handle LTR/RTL associativity?
|
||||
while (Peek().IsBinaryOperator() && BinaryOperatorPrecedence(Peek().type) < precedence)
|
||||
while (true)
|
||||
{
|
||||
const Token tok = Chew();
|
||||
ParseResult rhs = ParseBinary(BinaryOperatorPrecedence(tok.type));
|
||||
if (rhs.status == ParseStatus::SyntaxError)
|
||||
const Token op = Peek();
|
||||
if (op.IsBinaryOperator() && OperatorPrecedence(op.type) < precedence)
|
||||
{
|
||||
return rhs;
|
||||
Chew();
|
||||
ParseResult rhs = ParseInfixOperations(OperatorPrecedence(op.type));
|
||||
if (rhs.status == ParseStatus::SyntaxError)
|
||||
return rhs;
|
||||
|
||||
expr = std::make_unique<BinaryExpression>(op.type, std::move(expr), std::move(rhs.expr));
|
||||
}
|
||||
else if (op.type == TOK_QUESTION && OperatorPrecedence(TOK_QUESTION) <= precedence)
|
||||
{
|
||||
// Handle conditional operator: (a ? b : c)
|
||||
Chew();
|
||||
auto true_result = ParseInfixOperations(OperatorPrecedence(op.type));
|
||||
if (true_result.status != ParseStatus::Successful)
|
||||
return true_result;
|
||||
|
||||
expr = std::make_unique<BinaryExpression>(tok.type, std::move(expr), std::move(rhs.expr));
|
||||
const Token should_be_colon = Chew();
|
||||
if (should_be_colon.type != TOK_COLON)
|
||||
return ParseResult::MakeErrorResult(should_be_colon,
|
||||
Common::GetStringT("Expected colon."));
|
||||
|
||||
auto false_result = ParseInfixOperations(OperatorPrecedence(op.type));
|
||||
if (false_result.status != ParseStatus::Successful)
|
||||
return false_result;
|
||||
|
||||
auto conditional = MakeFunctionExpression("if");
|
||||
std::vector<std::unique_ptr<Expression>> args;
|
||||
args.emplace_back(std::move(expr));
|
||||
args.emplace_back(std::move(true_result.expr));
|
||||
args.emplace_back(std::move(false_result.expr));
|
||||
conditional->SetArguments(std::move(args));
|
||||
expr = std::move(conditional);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ParseResult::MakeSuccessfulResult(std::move(expr));
|
||||
}
|
||||
|
||||
|
@ -950,7 +984,7 @@ private:
|
|||
return ParseResult::MakeSuccessfulResult(std::make_unique<HotkeyExpression>(std::move(inputs)));
|
||||
}
|
||||
|
||||
ParseResult ParseToplevel() { return ParseBinary(); }
|
||||
ParseResult ParseToplevel() { return ParseInfixOperations(); }
|
||||
}; // namespace ExpressionParser
|
||||
|
||||
ParseResult ParseTokens(const std::vector<Token>& tokens)
|
||||
|
|
|
@ -26,6 +26,8 @@ enum TokenType
|
|||
TOK_BAREWORD,
|
||||
TOK_COMMENT,
|
||||
TOK_HOTKEY,
|
||||
TOK_QUESTION,
|
||||
TOK_COLON,
|
||||
// Binary Ops:
|
||||
TOK_BINARY_OPS_BEGIN,
|
||||
TOK_AND = TOK_BINARY_OPS_BEGIN,
|
||||
|
|
|
@ -688,7 +688,7 @@ void Shutdown()
|
|||
StopScanThread();
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
#if LIBUSB_API_HAS_HOTPLUG
|
||||
if (s_libusb_context->IsValid() && s_libusb_hotplug_enabled)
|
||||
if (s_libusb_context && s_libusb_context->IsValid() && s_libusb_hotplug_enabled)
|
||||
libusb_hotplug_deregister_callback(*s_libusb_context, s_hotplug_handle);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/Image.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/AbstractStagingTexture.h"
|
||||
|
||||
static_assert(std::is_same_v<AbstractTexture::imgui_texture_id, ImTextureID>,
|
||||
"The typedef should match ImTextureID in imgui");
|
||||
|
||||
AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ public:
|
|||
explicit AbstractTexture(const TextureConfig& c);
|
||||
virtual ~AbstractTexture() = default;
|
||||
|
||||
// Support implicit conversion between AbstractTexture and ImTextureId
|
||||
using imgui_texture_id = unsigned long long;
|
||||
operator imgui_texture_id() const { return reinterpret_cast<imgui_texture_id>(this); }
|
||||
|
||||
virtual void CopyRectangleFromTexture(const AbstractTexture* src,
|
||||
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
|
||||
|
|
|
@ -109,8 +109,8 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti
|
|||
|
||||
if (msg.texture)
|
||||
{
|
||||
ImGui::Image(msg.texture.get(), ImVec2(static_cast<float>(msg.icon->width),
|
||||
static_cast<float>(msg.icon->height)));
|
||||
ImGui::Image(*msg.texture.get(), ImVec2(static_cast<float>(msg.icon->width),
|
||||
static_cast<float>(msg.icon->height)));
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ bool OnScreenUI::Initialize(u32 width, u32 height, float scale)
|
|||
font_tex->Load(0, font_tex_width, font_tex_height, font_tex_width, font_tex_pixels,
|
||||
sizeof(u32) * font_tex_width * font_tex_height);
|
||||
|
||||
io.Fonts->TexID = font_tex.get();
|
||||
io.Fonts->TexID = *font_tex.get();
|
||||
|
||||
m_imgui_textures.push_back(std::move(font_tex));
|
||||
}
|
||||
|
@ -371,8 +371,8 @@ void OnScreenUI::DrawChallengesAndLeaderboards()
|
|||
{
|
||||
for (auto& [name, texture] : m_challenge_texture_map)
|
||||
{
|
||||
ImGui::Image(texture.get(), ImVec2(static_cast<float>(texture->GetWidth()) * scale,
|
||||
static_cast<float>(texture->GetHeight()) * scale));
|
||||
ImGui::Image(*texture.get(), ImVec2(static_cast<float>(texture->GetWidth()) * scale,
|
||||
static_cast<float>(texture->GetHeight()) * scale));
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue