Improvements to source code editing.

- auto-complete box
- keyword completion is working (but the files defining keywords are nowhere to be found!)
- identifier tooltips
- function parameters tooltips

One thing that ISN'T working with autocomplete is you cannot dismiss the listbox using ESC key: we never get that key from ImGui for some reason.
This commit is contained in:
Artyom Shalkhakov 2025-02-20 00:25:10 -07:00
parent 8db093e99a
commit e83c3ae3df
7 changed files with 565 additions and 830 deletions

View file

@ -50,6 +50,11 @@ TextEditor::TextEditor()
, mLastClick(-1.0f)
, mKeyPressData(NULL)
, mKeyPressHandler(NULL)
, mKeyDownHandler(NULL)
, mMouseButtonDownHandler(NULL)
, mGetToolTipHandler(NULL)
, mSetFocus(false)
, mCursorScreenPos(0.0f, 0.0f)
{
SetPalette(GetDarkPalette());
SetLanguageDefinition(LanguageDefinition::HLSL());
@ -66,9 +71,12 @@ void TextEditor::Focus() {
mSetFocus = true;
}
void TextEditor::SetKeyPress(void* data, textEditKeyPress_t keyPress) {
void TextEditor::SetHandlers(void* data, textEditKeyPress_t keyPress, textEditKeyDown_t keyDown, textEditMouseButtonDown_t mouseButtonDown, textEditGetToolTip_t getToolTip) {
mKeyPressData = data;
mKeyPressHandler = keyPress;
mKeyDownHandler = keyDown;
mMouseButtonDownHandler = mouseButtonDown;
mGetToolTipHandler = getToolTip;
}
void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef)
@ -671,6 +679,16 @@ TextEditor::Line& TextEditor::InsertLine(int aIndex)
return result;
}
std::string TextEditor::GetWordBeforeCursor() const
{
auto c = GetCursorPosition();
if (c.mColumn > 2)
{
c.mColumn -= 2; // remove .
}
return GetWordAt(c);
}
std::string TextEditor::GetWordUnderCursor() const
{
auto c = GetCursorPosition();
@ -730,7 +748,11 @@ void TextEditor::HandleKeyboardInputs()
io.WantCaptureKeyboard = true;
io.WantTextInput = true;
if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Z))
if (mKeyDownHandler && mKeyDownHandler(mKeyPressData))
{
// already handled
}
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Z))
Undo();
else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGuiKey_Backspace))
Undo();
@ -759,7 +781,14 @@ void TextEditor::HandleKeyboardInputs()
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete))
Delete();
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Backspace))
{
Backspace();
if (mKeyPressHandler)
{
// allow the autosuggest to react to this event
mKeyPressHandler(mKeyPressData, ctrl, shift, alt, 0x08);
}
}
else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))
mOverwrite ^= true;
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert))
@ -780,8 +809,6 @@ void TextEditor::HandleKeyboardInputs()
EnterCharacter('\n', false);
else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab))
EnterCharacter('\t', shift);
else if (mKeyPressHandler && !mKeyPressHandler(mKeyPressData, ctrl, shift, alt))
return;
if (!IsReadOnly() && !io.InputQueueCharacters.empty())
{
@ -805,6 +832,11 @@ void TextEditor::HandleMouseInputs()
if (ImGui::IsWindowHovered())
{
if (mMouseButtonDownHandler && mMouseButtonDownHandler(mKeyPressData))
{
return;
}
if (!shift && !alt)
{
auto click = ImGui::IsMouseClicked(0);
@ -995,6 +1027,8 @@ void TextEditor::Render()
}
// Render the cursor
float cx = TextDistanceToLineStart(mState.mCursorPosition);
mCursorScreenPos = ImVec2(textScreenPos.x + cx - cursorScreenPos.x + scrollX, lineStartScreenPos.y - cursorScreenPos.y - scrollY);
if (focused)
{
auto timeEnd = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
@ -1003,7 +1037,6 @@ void TextEditor::Render()
{
float width = 1.0f;
auto cindex = GetCharacterIndex(mState.mCursorPosition);
float cx = TextDistanceToLineStart(mState.mCursorPosition);
if (mOverwrite && cindex < (int)line.size())
{
@ -1107,22 +1140,31 @@ void TextEditor::Render()
auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos()));
if (!id.empty())
{
auto it = mLanguageDefinition.mIdentifiers.find(id);
if (it != mLanguageDefinition.mIdentifiers.end())
{
char tip[512];
if (mGetToolTipHandler && mGetToolTipHandler(mKeyPressData, id.c_str(), tip, sizeof(tip))) {
ImGui::BeginTooltip();
ImGui::TextUnformatted(it->second.mDeclaration.c_str());
ImGui::TextUnformatted(tip);
ImGui::EndTooltip();
}
else
{
auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);
if (pi != mLanguageDefinition.mPreprocIdentifiers.end())
auto it = mLanguageDefinition.mIdentifiers.find(id);
if (it != mLanguageDefinition.mIdentifiers.end())
{
ImGui::BeginTooltip();
ImGui::TextUnformatted(pi->second.mDeclaration.c_str());
ImGui::TextUnformatted(it->second.mDeclaration.c_str());
ImGui::EndTooltip();
}
else
{
auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id);
if (pi != mLanguageDefinition.mPreprocIdentifiers.end())
{
ImGui::BeginTooltip();
ImGui::TextUnformatted(pi->second.mDeclaration.c_str());
ImGui::EndTooltip();
}
}
}
}
}
@ -1157,7 +1199,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
if (mHandleKeyboardInputs)
{
HandleKeyboardInputs();
ImGui::PushAllowKeyboardFocus(true);
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, false);
}
if (mHandleMouseInputs)
@ -1167,7 +1209,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder)
Render();
if (mHandleKeyboardInputs)
ImGui::PopAllowKeyboardFocus();
ImGui::PopItemFlag();
if (!mIgnoreImGuiChild)
ImGui::EndChild();
@ -1409,6 +1451,16 @@ void TextEditor::EnterCharacter(ImWchar aChar, bool aShift)
Colorize(coord.mLine - 1, 3);
EnsureCursorVisible();
if (mKeyPressHandler)
{
ImGuiIO& io = ImGui::GetIO();
auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
mKeyPressHandler(mKeyPressData, ctrl, shift, alt, aChar);
}
}
void TextEditor::SetReadOnly(bool aValue)
@ -1523,6 +1575,35 @@ void TextEditor::DeleteSelection()
Colorize(mState.mSelectionStart.mLine, 1);
}
void TextEditor::ReplaceSelection(const char *text)
{
if (IsReadOnly())
return;
if (text != nullptr && strlen(text) > 0)
{
UndoRecord u;
u.mBefore = mState;
if (HasSelection())
{
u.mRemoved = GetSelectedText();
u.mRemovedStart = mState.mSelectionStart;
u.mRemovedEnd = mState.mSelectionEnd;
DeleteSelection();
}
u.mAdded = text;
u.mAddedStart = GetActualCursorCoordinates();
InsertText(text);
u.mAddedEnd = GetActualCursorCoordinates();
u.mAfter = mState;
AddUndo(u);
}
}
void TextEditor::MoveUp(int aAmount, bool aSelect)
{
auto oldPos = mState.mCursorPosition;
@ -1648,6 +1729,37 @@ void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode)
EnsureCursorVisible();
}
bool TextEditor::PeekLeftIsWhiteSpace(int aAmount) {
if (mLines.empty())
return true;
auto oldPos = mState.mCursorPosition;
auto line = mState.mCursorPosition.mLine;
auto cindex = GetCharacterIndex(mState.mCursorPosition);
while (aAmount-- > 0)
{
if (cindex == 0)
{
return true;
}
else
{
--cindex;
if (cindex > 0)
{
if ((int)mLines.size() > line)
{
while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar))
--cindex;
}
}
}
}
return isspace(mLines[line][cindex].mChar);
}
void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode)
{
auto oldPos = mState.mCursorPosition;
@ -1944,6 +2056,20 @@ bool TextEditor::FindNext(const char* find, bool matchCase, bool matchWholeWords
return found;
}
int TextEditor::ReplaceAll(const char* find, const char* replace, bool matchCase, bool matchWholeWords) {
int replacements;
replacements = 0;
SetCursorPosition(Coordinates(0, 0));
while ( FindNext( find, matchCase, matchWholeWords, true ) ) {
DeleteSelection();
InsertText(replace);
replacements++;
}
return replacements;
}
void TextEditor::Delete()
{
assert(!mReadOnly);
@ -2360,7 +2486,7 @@ std::string TextEditor::GetSelectedText() const
return GetText(mState.mSelectionStart, mState.mSelectionEnd);
}
std::string TextEditor::GetCurrentLineText()const
std::string TextEditor::GetCurrentLineText() const
{
auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine);
return GetText(
@ -2368,6 +2494,10 @@ std::string TextEditor::GetCurrentLineText()const
Coordinates(mState.mCursorPosition.mLine, lineLength));
}
ImVec2 TextEditor::GetCursorScreenCoordinates() const {
return mCursorScreenPos;
}
void TextEditor::ProcessInputs()
{
}

View file

@ -13,7 +13,10 @@
class TextEditor
{
public:
typedef bool (*textEditKeyPress_t)(void* data, bool ctrl, bool shift, bool alt);
typedef void (*textEditKeyPress_t)(void* data, bool ctrl, bool shift, bool alt, int chr);
typedef bool (*textEditKeyDown_t)(void* data);
typedef bool (*textEditMouseButtonDown_t)(void* data);
typedef bool (*textEditGetToolTip_t)(void* data, const char *identifier, char *tip, size_t tipLength);
enum class PaletteIndex
{
@ -189,7 +192,7 @@ public:
void Focus();
void SetKeyPress(void *data, textEditKeyPress_t keyPress);
void SetHandlers(void *data, textEditKeyPress_t keyPress, textEditKeyDown_t keyDown, textEditMouseButtonDown_t mouseButtonDown, textEditGetToolTip_t getTooltip);
void SetLanguageDefinition(const LanguageDefinition& aLanguageDef);
const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }
@ -210,6 +213,8 @@ public:
std::string GetSelectedText() const;
std::string GetCurrentLineText()const;
ImVec2 GetCursorScreenCoordinates() const;
int GetTotalLines() const { return (int)mLines.size(); }
bool IsOverwrite() const { return mOverwrite; }
@ -242,9 +247,12 @@ public:
void InsertText(const std::string& aValue);
void InsertText(const char* aValue);
std::string GetWordBeforeCursor() const;
void MoveUp(int aAmount = 1, bool aSelect = false);
void MoveDown(int aAmount = 1, bool aSelect = false);
void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
bool PeekLeftIsWhiteSpace(int aAmount = 1);
void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
void MoveTop(bool aSelect = false);
void MoveBottom(bool aSelect = false);
@ -253,6 +261,7 @@ public:
bool SubstringMatch(const char* find, size_t findLen, bool matchCase, bool matchWholeWords, const TextEditor::Line line, int lineNum, size_t index);
bool FindNext(const char *str, bool matchCase, bool matchWholeWords, bool searchForward);
int ReplaceAll(const char *find, const char *replace, bool matchCase, bool matchWholeWords);
void SetSelectionStart(const Coordinates& aPosition);
void SetSelectionEnd(const Coordinates& aPosition);
@ -261,6 +270,7 @@ public:
void SelectAll();
bool HasSelection() const;
void DeleteSelection();
void ReplaceSelection(const char* text);
void Copy();
void Cut();
@ -284,6 +294,8 @@ public:
static const Palette& GetLightPalette();
static const Palette& GetRetroBluePalette();
std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
private:
typedef std::vector<std::pair<std::regex, PaletteIndex>> RegexList;
@ -336,7 +348,6 @@ private:
float TextDistanceToLineStart(const Coordinates& aFrom) const;
void EnsureCursorVisible();
int GetPageSize() const;
std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
Coordinates GetActualCursorCoordinates() const;
Coordinates SanitizeCoordinates(const Coordinates& aValue) const;
void Advance(Coordinates& aCoordinates) const;
@ -411,5 +422,10 @@ private:
void* mKeyPressData;
textEditKeyPress_t mKeyPressHandler;
textEditKeyDown_t mKeyDownHandler;
textEditMouseButtonDown_t mMouseButtonDownHandler;
textEditGetToolTip_t mGetToolTipHandler;
bool mSetFocus;
ImVec2 mCursorScreenPos;
};

View file

@ -118,14 +118,7 @@ void ScriptEditor::Draw()
ImGui::EndMenu();
}
/*
if ( ImGui::BeginMenu( "Edit" ) ) {
if ( ImGui::MenuItem( "Go To...", "Ctrl+G" ) ) {
OnEditGoToLine();
}
ImGui::EndMenu();
}*/
ImGui::EndMenuBar();
}

View file

@ -622,6 +622,93 @@ FindReplaceDialog::command_t FindReplaceDialog::Draw( const ImVec2 &pos, const I
return command;
}
MessageBoxDialog::MessageBoxDialog()
: message()
, choice(false)
, error(false)
, visible(false)
, acked(false)
, focus(false)
{
}
void MessageBoxDialog::Start( const char *_message, bool _choice, bool _error ) {
message = _message;
choice = _choice;
error = _error;
visible = true;
acked = false;
focus = true;
}
bool MessageBoxDialog::Draw( const ImVec2 &pos, const ImVec2 &size ) {
if ( !visible ) {
return false;
}
ImGuiStyle &style = ImGui::GetStyle();
ImVec2 textSize = ImGui::CalcTextSize( message.c_str() );
float windowHeight =
style.ChildBorderSize * 2.0f +
style.WindowPadding.y * 2.0f +
ImGui::GetFrameHeight() * 2.0f +
textSize.y;
float windowWidth =
style.ChildBorderSize * 2.0f +
style.WindowPadding.x * 2.0f +
textSize.x;
ImVec2 oldCursorPos = ImGui::GetCursorPos();
bool interacted = false;
ImGui::SetCursorPos(ImVec2(
pos.x + size.x * 0.5f - windowWidth * 0.5f,
pos.y + size.y * 0.5f - windowHeight * 0.5f));
if ( ImGui::BeginChild( "Message", ImVec2( windowWidth, windowHeight ), ImGuiChildFlags_Borders ) ) {
if ( error ) {
ImGui::TextColored( ImVec4(1, 0, 0, 1), "%s", message.c_str() );
} else {
ImGui::TextUnformatted( message.c_str() );
}
if ( focus ) {
ImGui::SetKeyboardFocusHere( -1 );
focus = false;
}
if ( choice ) {
if ( ImGui::Button( "Yes" ) ) {
acked = true;
interacted = true;
visible = false;
}
ImGui::SameLine();
if ( ImGui::Button( "No" ) ) {
acked = false;
interacted = true;
visible = false;
}
} else {
if ( ImGui::Button( "OK" ) ) {
visible = false;
interacted = true;
acked = true;
}
}
}
ImGui::EndChild();
ImGui::SetCursorPos( oldCursorPos );
return interacted;
}
} //namespace ImGuiTools

View file

@ -178,6 +178,24 @@ private:
bool focus;
};
class MessageBoxDialog {
public:
MessageBoxDialog();
void Start( const char *message, bool choice, bool error );
bool Draw( const ImVec2 &pos, const ImVec2 &size );
ID_INLINE bool Result() const { return acked; };
private:
idStr message;
bool choice;
bool error;
bool visible;
bool acked;
bool focus;
};
} //namespace ImGuiTools

File diff suppressed because it is too large Load diff

View file

@ -101,12 +101,7 @@ public:
void SetFunctionParmCallback( toolTipCallback_t callback );
void SetToolTipCallback( toolTipCallback_t callback );
void SetDefaultColor( const idVec3 &color );
void SetCommentColor( const idVec3 &color );
void SetStringColor( const idVec3 &color, const idVec3 &altColor = vec3_origin );
void SetLiteralColor( const idVec3 &color );
idVec3 GetBackColor( int charIndex ) const;
void GetCursorPos( int &line, int &column, int &character ) const;
@ -118,21 +113,12 @@ public:
void SetFocus();
public:
//virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const;
bool OnToolTipNotify();
//afx_msg UINT OnGetDlgCode();
bool OnChar( bool ctrl, bool shift, bool alt );
bool OnToolTipNotify( const char *ident, char *tooltip, size_t tooltipSize );
void OnChar( bool ctrl, bool shift, bool alt, int nChar );
bool OnKeyDown();
bool OnMouseButtonDown();
private:
//afx_msg void OnKeyDown(UINT nKey, UINT nRepCnt, UINT nFlags);
//afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
//afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
//afx_msg void OnSize(UINT nType, int cx, int cy);
//afx_msg void OnProtected(NMHDR* pNMHDR, LRESULT* pResult);
//afx_msg void OnChange();
//afx_msg void OnAutoCompleteListBoxChange();
//afx_msg void OnAutoCompleteListBoxDblClk();
void OnMouseWheel(float wheel);
void OnEditGoToLine();
void OnEditFindNext();
@ -146,6 +132,7 @@ private:
idStr errorText;
FindReplaceDialog findDlg;
GoToLineDialog gotoDlg;
MessageBoxDialog msgBoxDlg;
idStr findStr;
idStr replaceStr;
bool matchCase;
@ -193,20 +180,19 @@ private:
int stringColorLine;
int autoCompleteStart;
ImVec2 autoCompleteListBoxPos;
ImVec2 autoCompleteListBoxSize;
idStrList autoCompleteListBox;
idList<int> autoCompleteListBoxFiltered;
int autoCompleteListBoxSel;
idStr autoCompleteInput;
int autoCompleteLastKeyDownTime;
int funcParmToolTipStart;
ImVec2 funcParmToolTipPos;
ImVec2 funcParmToolTipSize;
idStr funcParmToolTip;
int bracedSection[2];
/*
CPoint mousePoint;
CToolTipCtrl* keyWordToolTip;
TCHAR* m_pchTip;
WCHAR* m_pwchTip;
*/
private:
void InitSyntaxHighlighting( void );
void SetCharType( int first, int last, int type );
@ -215,21 +201,14 @@ private:
int FindKeyWord( const char *keyWord, int length ) const;
bool GetNameBeforeCurrentSelection( idStr &name, int &charIndex ) const;
bool GetNameForMousePosition( idStr &name ) const;
void AutoCompleteInsertText( void );
void AutoCompleteUpdate( void );
void AutoCompleteShow( int charIndex );
void AutoCompleteShow( int columnIndex );
void AutoCompleteHide( void );
void ToolTipShow( int charIndex, const char *string );
void ToolTipHide( void );
bool BracedSectionStart( char braceStartChar, char braceEndChar );
bool BracedSectionEnd( char braceStartChar, char braceEndChar );
void BracedSectionAdjustEndTabs( void );
void BracedSectionShow( void );
void BracedSectionHide( void );
};
}