2021-08-29 14:18:45 +02:00
# include <hex/api/content_registry.hpp>
2022-07-02 16:22:38 +02:00
# include <hex/api/localization.hpp>
2021-06-07 18:13:54 +02:00
2022-05-27 20:42:07 +02:00
# include <hex/ui/view.hpp>
2021-08-29 22:15:18 +02:00
# include <hex/helpers/utils.hpp>
# include <hex/helpers/fmt.hpp>
2022-07-02 17:53:13 +02:00
# include <hex/helpers/logger.hpp>
2021-08-29 22:15:18 +02:00
2022-07-26 14:59:08 +02:00
# include <fonts/codicons_font.h>
2021-08-21 00:52:11 +02:00
# include <imgui.h>
# include <imgui_internal.h>
2021-12-31 01:10:06 +01:00
# include <hex/ui/imgui_imhex_extensions.h>
2021-08-21 00:52:11 +02:00
2021-06-07 18:13:54 +02:00
namespace hex : : plugin : : builtin {
2022-07-02 17:53:13 +02:00
static std : : string s_popupMessage ;
static std : : function < void ( ) > s_yesCallback , s_noCallback ;
static u32 s_selectableFileIndex ;
static std : : vector < std : : fs : : path > s_selectableFiles ;
static std : : function < void ( std : : fs : : path ) > s_selectableFileOpenCallback ;
static std : : vector < nfdfilteritem_t > s_selectableFilesValidExtensions ;
2022-05-27 20:42:07 +02:00
static void drawGlobalPopups ( ) {
// "Are you sure you want to exit?" Popup
if ( ImGui : : BeginPopupModal ( " hex.builtin.popup.exit_application.title " _lang , nullptr , ImGuiWindowFlags_AlwaysAutoResize ) ) {
ImGui : : NewLine ( ) ;
ImGui : : TextUnformatted ( " hex.builtin.popup.exit_application.desc " _lang ) ;
ImGui : : NewLine ( ) ;
View : : confirmButtons (
" hex.builtin.common.yes " _lang , " hex.builtin.common.no " _lang , [ ] { ImHexApi : : Common : : closeImHex ( true ) ; } , [ ] { ImGui : : CloseCurrentPopup ( ) ; } ) ;
if ( ImGui : : IsKeyDown ( ImGui : : GetKeyIndex ( ImGuiKey_Escape ) ) )
ImGui : : CloseCurrentPopup ( ) ;
ImGui : : EndPopup ( ) ;
}
2022-07-02 17:53:13 +02:00
auto windowSize = ImHexApi : : System : : getMainWindowSize ( ) ;
// Info popup
ImGui : : SetNextWindowSizeConstraints ( scaled ( ImVec2 ( 400 , 100 ) ) , scaled ( ImVec2 ( 600 , 300 ) ) ) ;
if ( ImGui : : BeginPopupModal ( " hex.builtin.common.info " _lang , nullptr , ImGuiWindowFlags_AlwaysAutoResize ) ) {
ImGui : : TextFormattedWrapped ( " {} " , s_popupMessage . c_str ( ) ) ;
ImGui : : NewLine ( ) ;
ImGui : : Separator ( ) ;
if ( ImGui : : Button ( " hex.builtin.common.okay " _lang ) | | ImGui : : IsKeyDown ( ImGuiKey_Escape ) )
ImGui : : CloseCurrentPopup ( ) ;
ImGui : : SetWindowPos ( ( windowSize - ImGui : : GetWindowSize ( ) ) / 2 , ImGuiCond_Appearing ) ;
ImGui : : EndPopup ( ) ;
}
// Error popup
ImGui : : SetNextWindowSizeConstraints ( scaled ( ImVec2 ( 400 , 100 ) ) , scaled ( ImVec2 ( 600 , 300 ) ) ) ;
if ( ImGui : : BeginPopupModal ( " hex.builtin.common.error " _lang , nullptr , ImGuiWindowFlags_AlwaysAutoResize ) ) {
ImGui : : TextFormattedWrapped ( " {} " , s_popupMessage . c_str ( ) ) ;
ImGui : : NewLine ( ) ;
ImGui : : Separator ( ) ;
if ( ImGui : : Button ( " hex.builtin.common.okay " _lang ) | | ImGui : : IsKeyDown ( ImGuiKey_Escape ) )
ImGui : : CloseCurrentPopup ( ) ;
ImGui : : SetWindowPos ( ( windowSize - ImGui : : GetWindowSize ( ) ) / 2 , ImGuiCond_Appearing ) ;
ImGui : : EndPopup ( ) ;
}
// Fatal error popup
ImGui : : SetNextWindowSizeConstraints ( scaled ( ImVec2 ( 400 , 100 ) ) , scaled ( ImVec2 ( 600 , 300 ) ) ) ;
if ( ImGui : : BeginPopupModal ( " hex.builtin.common.fatal " _lang , nullptr , ImGuiWindowFlags_AlwaysAutoResize ) ) {
ImGui : : TextFormattedWrapped ( " {} " , s_popupMessage . c_str ( ) ) ;
ImGui : : NewLine ( ) ;
ImGui : : Separator ( ) ;
if ( ImGui : : Button ( " hex.builtin.common.okay " _lang ) | | ImGui : : IsKeyDown ( ImGuiKey_Escape ) ) {
ImHexApi : : Common : : closeImHex ( ) ;
ImGui : : CloseCurrentPopup ( ) ;
}
ImGui : : SetWindowPos ( ( windowSize - ImGui : : GetWindowSize ( ) ) / 2 , ImGuiCond_Appearing ) ;
ImGui : : EndPopup ( ) ;
}
// Yes/No question popup
ImGui : : SetNextWindowSizeConstraints ( scaled ( ImVec2 ( 400 , 100 ) ) , scaled ( ImVec2 ( 600 , 300 ) ) ) ;
if ( ImGui : : BeginPopupModal ( " hex.builtin.common.question " _lang , nullptr , ImGuiWindowFlags_AlwaysAutoResize ) ) {
ImGui : : TextFormattedWrapped ( " {} " , s_popupMessage . c_str ( ) ) ;
ImGui : : NewLine ( ) ;
ImGui : : Separator ( ) ;
View : : confirmButtons (
" hex.builtin.common.yes " _lang , " hex.builtin.common.no " _lang , [ ] {
s_yesCallback ( ) ;
ImGui : : CloseCurrentPopup ( ) ; } , [ ] {
s_noCallback ( ) ;
ImGui : : CloseCurrentPopup ( ) ; } ) ;
ImGui : : SetWindowPos ( ( windowSize - ImGui : : GetWindowSize ( ) ) / 2 , ImGuiCond_Appearing ) ;
ImGui : : EndPopup ( ) ;
}
// File chooser popup
bool opened = true ;
ImGui : : SetNextWindowPos ( ImGui : : GetMainViewport ( ) - > GetCenter ( ) , ImGuiCond_Appearing , ImVec2 ( 0.5F , 0.5F ) ) ;
if ( ImGui : : BeginPopupModal ( " hex.builtin.common.choose_file " _lang , & opened , ImGuiWindowFlags_AlwaysAutoResize ) ) {
if ( ImGui : : BeginListBox ( " ##files " , ImVec2 ( 300 _scaled , 0 ) ) ) {
u32 index = 0 ;
for ( auto & path : s_selectableFiles ) {
2022-07-07 23:29:50 +02:00
ImGui : : PushID ( index ) ;
2022-07-02 17:53:13 +02:00
if ( ImGui : : Selectable ( path . filename ( ) . string ( ) . c_str ( ) , index = = s_selectableFileIndex ) )
s_selectableFileIndex = index ;
2022-07-07 23:29:50 +02:00
ImGui : : PopID ( ) ;
2022-07-02 17:53:13 +02:00
index + + ;
}
ImGui : : EndListBox ( ) ;
}
if ( ImGui : : Button ( " hex.builtin.common.open " _lang ) ) {
s_selectableFileOpenCallback ( s_selectableFiles [ s_selectableFileIndex ] ) ;
ImGui : : CloseCurrentPopup ( ) ;
}
ImGui : : SameLine ( ) ;
if ( ImGui : : Button ( " hex.builtin.common.browse " _lang ) ) {
fs : : openFileBrowser ( fs : : DialogMode : : Open , s_selectableFilesValidExtensions , [ ] ( const auto & path ) {
s_selectableFileOpenCallback ( path ) ;
ImGui : : CloseCurrentPopup ( ) ;
} ) ;
}
ImGui : : EndPopup ( ) ;
}
2022-05-27 20:42:07 +02:00
}
void addGlobalUIItems ( ) {
EventManager : : subscribe < EventFrameEnd > ( drawGlobalPopups ) ;
2022-07-02 17:53:13 +02:00
EventManager : : subscribe < RequestShowInfoPopup > ( [ ] ( const std : : string & message ) {
s_popupMessage = message ;
ImHexApi : : Tasks : : doLater ( [ ] { ImGui : : OpenPopup ( " hex.builtin.common.info " _lang ) ; } ) ;
} ) ;
EventManager : : subscribe < RequestShowErrorPopup > ( [ ] ( const std : : string & message ) {
s_popupMessage = message ;
ImHexApi : : Tasks : : doLater ( [ ] { ImGui : : OpenPopup ( " hex.builtin.common.error " _lang ) ; } ) ;
} ) ;
EventManager : : subscribe < RequestShowFatalErrorPopup > ( [ ] ( const std : : string & message ) {
s_popupMessage = message ;
ImHexApi : : Tasks : : doLater ( [ ] { ImGui : : OpenPopup ( " hex.builtin.common.fatal " _lang ) ; } ) ;
} ) ;
EventManager : : subscribe < RequestShowYesNoQuestionPopup > ( [ ] ( const std : : string & message , const std : : function < void ( ) > & yesCallback , const std : : function < void ( ) > & noCallback ) {
s_popupMessage = message ;
s_yesCallback = yesCallback ;
s_noCallback = noCallback ;
ImHexApi : : Tasks : : doLater ( [ ] { ImGui : : OpenPopup ( " hex.builtin.common.question " _lang ) ; } ) ;
} ) ;
EventManager : : subscribe < RequestShowFileChooserPopup > ( [ ] ( const std : : vector < std : : fs : : path > & paths , const std : : vector < nfdfilteritem_t > & validExtensions , const std : : function < void ( std : : fs : : path ) > & callback ) {
s_selectableFileIndex = 0 ;
s_selectableFiles = paths ;
s_selectableFilesValidExtensions = validExtensions ;
s_selectableFileOpenCallback = callback ;
ImHexApi : : Tasks : : doLater ( [ ] { ImGui : : OpenPopup ( " hex.builtin.common.choose_file " _lang ) ; } ) ;
} ) ;
2022-05-27 20:42:07 +02:00
}
2021-06-07 18:13:54 +02:00
void addFooterItems ( ) {
2021-12-13 22:58:23 +01:00
if ( hex : : isProcessElevated ( ) ) {
ContentRegistry : : Interface : : addFooterItem ( [ ] {
ImGui : : TextUnformatted ( ICON_VS_SHIELD ) ;
} ) ;
}
2021-06-07 18:13:54 +02:00
ContentRegistry : : Interface : : addFooterItem ( [ ] {
static float framerate = 0 ;
if ( ImGui : : HasSecondPassed ( ) ) {
2021-07-27 21:50:49 +02:00
framerate = 1.0F / ImGui : : GetIO ( ) . DeltaTime ;
2021-06-07 18:13:54 +02:00
}
2021-12-31 01:10:06 +01:00
ImGui : : TextFormatted ( " FPS {0:2}.{1:02} " , u32 ( framerate ) , u32 ( framerate * 100 ) % 100 ) ;
2021-12-16 23:48:52 +01:00
} ) ;
ContentRegistry : : Interface : : addFooterItem ( [ ] {
2022-02-01 22:09:44 +01:00
size_t taskCount = 0 ;
2021-12-16 23:48:52 +01:00
double taskProgress = 0.0 ;
std : : string taskName ;
{
2022-02-01 18:09:40 +01:00
std : : scoped_lock lock ( Task : : getTaskMutex ( ) ) ;
2021-12-16 23:48:52 +01:00
2022-02-01 18:09:40 +01:00
taskCount = Task : : getRunningTasks ( ) . size ( ) ;
2021-12-16 23:48:52 +01:00
if ( taskCount > 0 ) {
2022-02-01 18:09:40 +01:00
auto frontTask = Task : : getRunningTasks ( ) . front ( ) ;
2022-02-01 22:09:44 +01:00
taskProgress = frontTask - > getProgress ( ) ;
taskName = frontTask - > getName ( ) ;
2021-12-16 23:48:52 +01:00
}
}
if ( taskCount > 0 ) {
2022-08-04 11:00:49 +02:00
ImGui : : TextSpinner ( hex : : format ( " ({}) " , taskCount ) . c_str ( ) ) ;
2021-12-16 23:48:52 +01:00
ImGui : : SameLine ( ) ;
2022-01-11 23:48:18 +01:00
ImGui : : SmallProgressBar ( taskProgress , ( ImGui : : GetCurrentWindow ( ) - > MenuBarHeight ( ) - 10 _scaled ) / 2.0 ) ;
2021-12-16 23:48:52 +01:00
ImGui : : InfoTooltip ( taskName . c_str ( ) ) ;
}
2021-06-07 18:13:54 +02:00
} ) ;
}
2021-08-21 00:52:11 +02:00
void addToolbarItems ( ) {
ContentRegistry : : Interface : : addToolbarItem ( [ ] {
2022-03-13 17:46:59 +01:00
auto provider = ImHexApi : : Provider : : get ( ) ;
bool providerValid = provider ! = nullptr ;
2022-07-29 11:35:29 +02:00
bool tasksRunning = Task : : getRunningTaskCount ( ) > 0 ;
2021-08-21 13:53:50 +02:00
// Undo
2022-05-27 20:42:07 +02:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > canUndo ( ) ) ;
{
2021-10-07 11:34:46 +02:00
if ( ImGui : : ToolBarButton ( ICON_VS_DISCARD , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2021-08-21 13:53:50 +02:00
provider - > undo ( ) ;
2022-05-27 20:42:07 +02:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 13:53:50 +02:00
// Redo
2022-05-27 20:42:07 +02:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > canRedo ( ) ) ;
{
2021-10-07 11:34:46 +02:00
if ( ImGui : : ToolBarButton ( ICON_VS_REDO , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2021-08-21 13:53:50 +02:00
provider - > redo ( ) ;
2022-05-27 20:42:07 +02:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 00:52:11 +02:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
2022-07-29 11:35:29 +02:00
ImGui : : BeginDisabled ( tasksRunning ) ;
{
// Create new file
if ( ImGui : : ToolBarButton ( ICON_VS_FILE , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarGray ) ) )
EventManager : : post < RequestOpenWindow > ( " Create File " ) ;
2021-08-21 13:53:50 +02:00
2022-07-29 11:35:29 +02:00
// Open file
if ( ImGui : : ToolBarButton ( ICON_VS_FOLDER_OPENED , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBrown ) ) )
EventManager : : post < RequestOpenWindow > ( " Open File " ) ;
}
ImGui : : EndDisabled ( ) ;
2021-08-21 00:52:11 +02:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
2021-08-21 13:53:50 +02:00
// Save file
2022-05-27 20:42:07 +02:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > isWritable ( ) | | ! provider - > isSavable ( ) ) ;
{
2021-10-07 11:34:46 +02:00
if ( ImGui : : ToolBarButton ( ICON_VS_SAVE , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2021-08-21 13:53:50 +02:00
provider - > save ( ) ;
2022-05-27 20:42:07 +02:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 13:53:50 +02:00
// Save file as
2022-05-27 20:42:07 +02:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > isSavable ( ) ) ;
{
2021-10-07 11:34:46 +02:00
if ( ImGui : : ToolBarButton ( ICON_VS_SAVE_AS , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2022-03-27 00:01:28 +01:00
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & provider ] ( auto path ) {
2021-08-21 13:53:50 +02:00
provider - > saveAs ( path ) ;
} ) ;
2022-05-27 20:42:07 +02:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 13:53:50 +02:00
2021-08-21 00:52:11 +02:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
2021-08-21 13:53:50 +02:00
// Create bookmark
2022-05-27 20:42:07 +02:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > isReadable ( ) | | ! ImHexApi : : HexEditor : : isSelectionValid ( ) ) ;
{
2021-10-07 11:34:46 +02:00
if ( ImGui : : ToolBarButton ( ICON_VS_BOOKMARK , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarGreen ) ) ) {
2022-05-27 20:42:07 +02:00
auto region = ImHexApi : : HexEditor : : getSelection ( ) ;
2021-08-21 13:53:50 +02:00
2022-05-27 20:42:07 +02:00
if ( region . has_value ( ) )
2022-07-23 21:17:17 +02:00
ImHexApi : : Bookmarks : : add ( region - > getStartAddress ( ) , region - > getSize ( ) , { } , { } ) ;
2021-08-21 13:53:50 +02:00
}
2022-05-27 20:42:07 +02:00
}
ImGui : : EndDisabled ( ) ;
2021-09-21 02:29:54 +02:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
ImGui : : Spacing ( ) ;
2022-07-30 21:25:18 +02:00
ImGui : : Spacing ( ) ;
ImGui : : Spacing ( ) ;
2021-09-21 02:29:54 +02:00
// Provider switcher
2022-07-29 11:35:29 +02:00
ImGui : : BeginDisabled ( ! providerValid | | tasksRunning ) ;
2022-05-27 20:42:07 +02:00
{
2021-09-21 02:29:54 +02:00
auto & providers = ImHexApi : : Provider : : getProviders ( ) ;
2022-07-30 21:25:18 +02:00
ImGui : : PushStyleColor ( ImGuiCol_TabActive , ImGui : : GetColorU32 ( ImGuiCol_MenuBarBg ) ) ;
ImGui : : PushStyleColor ( ImGuiCol_TabUnfocusedActive , ImGui : : GetColorU32 ( ImGuiCol_MenuBarBg ) ) ;
2022-07-31 11:09:20 +02:00
auto providerSelectorVisible = ImGui : : BeginTabBar ( " provider_switcher " , ImGuiTabBarFlags_FittingPolicyScroll | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs ) ;
2022-07-30 21:25:18 +02:00
ImGui : : PopStyleColor ( 2 ) ;
2021-09-21 02:29:54 +02:00
2022-07-30 21:25:18 +02:00
if ( providerSelectorVisible ) {
2022-03-27 00:01:28 +01:00
for ( size_t i = 0 ; i < providers . size ( ) ; i + + ) {
2022-07-31 10:55:10 +02:00
auto & tabProvider = providers [ i ] ;
2022-07-30 21:25:18 +02:00
bool open = true ;
2022-07-31 10:55:10 +02:00
ImGui : : PushID ( tabProvider ) ;
if ( ImGui : : BeginTabItem ( tabProvider - > getName ( ) . c_str ( ) , & open ) ) {
2022-02-01 18:09:40 +01:00
ImHexApi : : Provider : : setCurrentProvider ( i ) ;
2022-07-30 21:25:18 +02:00
ImGui : : EndTabItem ( ) ;
2021-09-21 02:29:54 +02:00
}
2022-07-31 10:55:10 +02:00
ImGui : : PopID ( ) ;
2021-09-21 02:29:54 +02:00
2022-07-30 21:25:18 +02:00
if ( ! open ) {
ImHexApi : : Provider : : remove ( providers [ i ] ) ;
break ;
}
}
ImGui : : EndTabBar ( ) ;
2021-09-21 02:29:54 +02:00
}
2022-05-27 20:42:07 +02:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 00:52:11 +02:00
} ) ;
2022-05-27 20:42:07 +02:00
2021-08-21 00:52:11 +02:00
}
2022-07-02 17:53:13 +02:00
void handleBorderlessWindowMode ( ) {
// Intel's OpenGL driver has weird bugs that cause the drawn window to be offset to the bottom right.
// This can be fixed by either using Mesa3D's OpenGL Software renderer or by simply disabling it.
// If you want to try if it works anyways on your GPU, set the hex.builtin.setting.interface.force_borderless_window_mode setting to 1
if ( ImHexApi : : System : : isBorderlessWindowModeEnabled ( ) ) {
bool isIntelGPU = hex : : containsIgnoreCase ( ImHexApi : : System : : getGPUVendor ( ) , " Intel " ) ;
ImHexApi : : System : : impl : : setBorderlessWindowMode ( ! isIntelGPU ) ;
if ( isIntelGPU )
log : : warn ( " Intel GPU detected! Intel's OpenGL driver has bugs that can cause issues when using ImHex. If you experience any rendering bugs, please try the Mesa3D Software Renderer " ) ;
}
}
2021-06-07 18:13:54 +02:00
}