2021-08-29 12:18:45 +00:00
# include <hex/api/content_registry.hpp>
2022-07-02 14:22:38 +00:00
# include <hex/api/localization.hpp>
2021-06-07 16:13:54 +00:00
2022-05-27 18:42:07 +00:00
# include <hex/ui/view.hpp>
2021-08-29 20:15:18 +00:00
# include <hex/helpers/utils.hpp>
# include <hex/helpers/fmt.hpp>
2022-07-02 15:53:13 +00:00
# include <hex/helpers/logger.hpp>
2021-08-29 20:15:18 +00:00
2022-07-26 12:59:08 +00:00
# include <fonts/codicons_font.h>
2021-08-20 22:52:11 +00:00
# include <imgui.h>
# include <imgui_internal.h>
2021-12-31 00:10:06 +00:00
# include <hex/ui/imgui_imhex_extensions.h>
2021-08-20 22:52:11 +00:00
2021-06-07 16:13:54 +00:00
namespace hex : : plugin : : builtin {
2022-07-02 15:53:13 +00: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 18:42:07 +00: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 ( ) ;
2022-08-08 19:23:52 +00:00
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 ( ) ;
}
// "Are you sure you want to close provider?" Popup
if ( ImGui : : BeginPopupModal ( " hex.builtin.popup.close_provider.title " _lang , nullptr , ImGuiWindowFlags_AlwaysAutoResize ) ) {
ImGui : : NewLine ( ) ;
ImGui : : TextUnformatted ( " hex.builtin.popup.close_provider.desc " _lang ) ;
ImGui : : NewLine ( ) ;
View : : confirmButtons ( " hex.builtin.common.yes " _lang , " hex.builtin.common.no " _lang ,
[ ] { ImHexApi : : Provider : : remove ( ImHexApi : : Provider : : impl : : getClosingProvider ( ) , true ) ; ImGui : : CloseCurrentPopup ( ) ; } ,
[ ] { ImGui : : CloseCurrentPopup ( ) ; }
) ;
2022-05-27 18:42:07 +00:00
if ( ImGui : : IsKeyDown ( ImGui : : GetKeyIndex ( ImGuiKey_Escape ) ) )
ImGui : : CloseCurrentPopup ( ) ;
ImGui : : EndPopup ( ) ;
}
2022-07-02 15:53:13 +00: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 21:29:50 +00:00
ImGui : : PushID ( index ) ;
2022-07-02 15:53:13 +00:00
if ( ImGui : : Selectable ( path . filename ( ) . string ( ) . c_str ( ) , index = = s_selectableFileIndex ) )
s_selectableFileIndex = index ;
2022-07-07 21:29:50 +00:00
ImGui : : PopID ( ) ;
2022-07-02 15:53:13 +00: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 18:42:07 +00:00
}
void addGlobalUIItems ( ) {
EventManager : : subscribe < EventFrameEnd > ( drawGlobalPopups ) ;
2022-07-02 15:53:13 +00: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 18:42:07 +00:00
}
2021-06-07 16:13:54 +00:00
void addFooterItems ( ) {
2021-12-13 21:58:23 +00:00
if ( hex : : isProcessElevated ( ) ) {
ContentRegistry : : Interface : : addFooterItem ( [ ] {
ImGui : : TextUnformatted ( ICON_VS_SHIELD ) ;
} ) ;
}
2021-06-07 16:13:54 +00:00
ContentRegistry : : Interface : : addFooterItem ( [ ] {
static float framerate = 0 ;
if ( ImGui : : HasSecondPassed ( ) ) {
2021-07-27 19:50:49 +00:00
framerate = 1.0F / ImGui : : GetIO ( ) . DeltaTime ;
2021-06-07 16:13:54 +00:00
}
2021-12-31 00:10:06 +00:00
ImGui : : TextFormatted ( " FPS {0:2}.{1:02} " , u32 ( framerate ) , u32 ( framerate * 100 ) % 100 ) ;
2021-12-16 22:48:52 +00:00
} ) ;
ContentRegistry : : Interface : : addFooterItem ( [ ] {
2022-02-01 21:09:44 +00:00
size_t taskCount = 0 ;
2021-12-16 22:48:52 +00:00
double taskProgress = 0.0 ;
std : : string taskName ;
{
2022-02-01 17:09:40 +00:00
std : : scoped_lock lock ( Task : : getTaskMutex ( ) ) ;
2021-12-16 22:48:52 +00:00
2022-02-01 17:09:40 +00:00
taskCount = Task : : getRunningTasks ( ) . size ( ) ;
2021-12-16 22:48:52 +00:00
if ( taskCount > 0 ) {
2022-02-01 17:09:40 +00:00
auto frontTask = Task : : getRunningTasks ( ) . front ( ) ;
2022-02-01 21:09:44 +00:00
taskProgress = frontTask - > getProgress ( ) ;
taskName = frontTask - > getName ( ) ;
2021-12-16 22:48:52 +00:00
}
}
if ( taskCount > 0 ) {
2022-08-04 09:00:49 +00:00
ImGui : : TextSpinner ( hex : : format ( " ({}) " , taskCount ) . c_str ( ) ) ;
2021-12-16 22:48:52 +00:00
ImGui : : SameLine ( ) ;
2022-01-11 22:48:18 +00:00
ImGui : : SmallProgressBar ( taskProgress , ( ImGui : : GetCurrentWindow ( ) - > MenuBarHeight ( ) - 10 _scaled ) / 2.0 ) ;
2021-12-16 22:48:52 +00:00
ImGui : : InfoTooltip ( taskName . c_str ( ) ) ;
}
2021-06-07 16:13:54 +00:00
} ) ;
}
2021-08-20 22:52:11 +00:00
void addToolbarItems ( ) {
ContentRegistry : : Interface : : addToolbarItem ( [ ] {
2022-03-13 16:46:59 +00:00
auto provider = ImHexApi : : Provider : : get ( ) ;
bool providerValid = provider ! = nullptr ;
2022-07-29 09:35:29 +00:00
bool tasksRunning = Task : : getRunningTaskCount ( ) > 0 ;
2021-08-21 11:53:50 +00:00
// Undo
2022-05-27 18:42:07 +00:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > canUndo ( ) ) ;
{
2021-10-07 09:34:46 +00:00
if ( ImGui : : ToolBarButton ( ICON_VS_DISCARD , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2021-08-21 11:53:50 +00:00
provider - > undo ( ) ;
2022-05-27 18:42:07 +00:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 11:53:50 +00:00
// Redo
2022-05-27 18:42:07 +00:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > canRedo ( ) ) ;
{
2021-10-07 09:34:46 +00:00
if ( ImGui : : ToolBarButton ( ICON_VS_REDO , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2021-08-21 11:53:50 +00:00
provider - > redo ( ) ;
2022-05-27 18:42:07 +00:00
}
ImGui : : EndDisabled ( ) ;
2021-08-20 22:52:11 +00:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
2022-07-29 09:35:29 +00: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 11:53:50 +00:00
2022-07-29 09:35:29 +00:00
// Open file
if ( ImGui : : ToolBarButton ( ICON_VS_FOLDER_OPENED , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBrown ) ) )
EventManager : : post < RequestOpenWindow > ( " Open File " ) ;
}
ImGui : : EndDisabled ( ) ;
2021-08-20 22:52:11 +00:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
2021-08-21 11:53:50 +00:00
// Save file
2022-05-27 18:42:07 +00:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > isWritable ( ) | | ! provider - > isSavable ( ) ) ;
{
2021-10-07 09:34:46 +00:00
if ( ImGui : : ToolBarButton ( ICON_VS_SAVE , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2021-08-21 11:53:50 +00:00
provider - > save ( ) ;
2022-05-27 18:42:07 +00:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 11:53:50 +00:00
// Save file as
2022-05-27 18:42:07 +00:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > isSavable ( ) ) ;
{
2021-10-07 09:34:46 +00:00
if ( ImGui : : ToolBarButton ( ICON_VS_SAVE_AS , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarBlue ) ) )
2022-03-26 23:01:28 +00:00
fs : : openFileBrowser ( fs : : DialogMode : : Save , { } , [ & provider ] ( auto path ) {
2021-08-21 11:53:50 +00:00
provider - > saveAs ( path ) ;
} ) ;
2022-05-27 18:42:07 +00:00
}
ImGui : : EndDisabled ( ) ;
2021-08-21 11:53:50 +00:00
2021-08-20 22:52:11 +00:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
2021-08-21 11:53:50 +00:00
// Create bookmark
2022-05-27 18:42:07 +00:00
ImGui : : BeginDisabled ( ! providerValid | | ! provider - > isReadable ( ) | | ! ImHexApi : : HexEditor : : isSelectionValid ( ) ) ;
{
2021-10-07 09:34:46 +00:00
if ( ImGui : : ToolBarButton ( ICON_VS_BOOKMARK , ImGui : : GetCustomColorVec4 ( ImGuiCustomCol_ToolbarGreen ) ) ) {
2022-05-27 18:42:07 +00:00
auto region = ImHexApi : : HexEditor : : getSelection ( ) ;
2021-08-21 11:53:50 +00:00
2022-05-27 18:42:07 +00:00
if ( region . has_value ( ) )
2022-07-23 19:17:17 +00:00
ImHexApi : : Bookmarks : : add ( region - > getStartAddress ( ) , region - > getSize ( ) , { } , { } ) ;
2021-08-21 11:53:50 +00:00
}
2022-05-27 18:42:07 +00:00
}
ImGui : : EndDisabled ( ) ;
2021-09-21 00:29:54 +00:00
ImGui : : SeparatorEx ( ImGuiSeparatorFlags_Vertical ) ;
ImGui : : Spacing ( ) ;
2022-07-30 19:25:18 +00:00
ImGui : : Spacing ( ) ;
ImGui : : Spacing ( ) ;
2021-09-21 00:29:54 +00:00
// Provider switcher
2022-07-29 09:35:29 +00:00
ImGui : : BeginDisabled ( ! providerValid | | tasksRunning ) ;
2022-05-27 18:42:07 +00:00
{
2021-09-21 00:29:54 +00:00
auto & providers = ImHexApi : : Provider : : getProviders ( ) ;
2022-07-30 19:25:18 +00:00
ImGui : : PushStyleColor ( ImGuiCol_TabActive , ImGui : : GetColorU32 ( ImGuiCol_MenuBarBg ) ) ;
ImGui : : PushStyleColor ( ImGuiCol_TabUnfocusedActive , ImGui : : GetColorU32 ( ImGuiCol_MenuBarBg ) ) ;
2022-07-31 09:09:20 +00:00
auto providerSelectorVisible = ImGui : : BeginTabBar ( " provider_switcher " , ImGuiTabBarFlags_FittingPolicyScroll | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs ) ;
2022-07-30 19:25:18 +00:00
ImGui : : PopStyleColor ( 2 ) ;
2021-09-21 00:29:54 +00:00
2022-07-30 19:25:18 +00:00
if ( providerSelectorVisible ) {
2022-03-26 23:01:28 +00:00
for ( size_t i = 0 ; i < providers . size ( ) ; i + + ) {
2022-07-31 08:55:10 +00:00
auto & tabProvider = providers [ i ] ;
2022-07-30 19:25:18 +00:00
bool open = true ;
2022-07-31 08:55:10 +00:00
ImGui : : PushID ( tabProvider ) ;
2022-08-08 19:23:52 +00:00
if ( ImGui : : BeginTabItem ( tabProvider - > getName ( ) . c_str ( ) , & open , provider - > isDirty ( ) ? ImGuiTabItemFlags_UnsavedDocument : ImGuiTabItemFlags_None ) ) {
2022-02-01 17:09:40 +00:00
ImHexApi : : Provider : : setCurrentProvider ( i ) ;
2022-07-30 19:25:18 +00:00
ImGui : : EndTabItem ( ) ;
2021-09-21 00:29:54 +00:00
}
2022-07-31 08:55:10 +00:00
ImGui : : PopID ( ) ;
2021-09-21 00:29:54 +00:00
2022-07-30 19:25:18 +00:00
if ( ! open ) {
ImHexApi : : Provider : : remove ( providers [ i ] ) ;
break ;
}
}
ImGui : : EndTabBar ( ) ;
2021-09-21 00:29:54 +00:00
}
2022-05-27 18:42:07 +00:00
}
ImGui : : EndDisabled ( ) ;
2021-08-20 22:52:11 +00:00
} ) ;
2022-05-27 18:42:07 +00:00
2021-08-20 22:52:11 +00:00
}
2022-07-02 15:53:13 +00: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 16:13:54 +00:00
}