Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\examples\olcPGE3_TextEntry.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\examples\olcPGE3_TextFontMap.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
Expand Down
3 changes: 3 additions & 0 deletions dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@
<ClCompile Include="..\..\..\examples\olcPGE3_SoftwareRendering.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\examples\olcPGE3_TextEntry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Image Include="..\..\..\examples\assets\minsanity_texture.png">
Expand Down
90 changes: 90 additions & 0 deletions dev/src/hw_keyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,46 @@ namespace olc::hw
}
}

void Keyboard::EnableTextEntry(const std::string_view sDefault, const std::string_view delim, const bool bLeaveMode)
{
sTextEntryString = sDefault;
vecTextEntryCommands.clear();
sTextEntryDelim = delim;
nTextEntryCursorPos = uint32_t(sTextEntryString.size());
bTextEntryMode = true;
bLeaveTextEntryModeUponDelimiter = bLeaveMode;
}

void Keyboard::DisableTextEntry()
{
bTextEntryMode = false;
}

bool Keyboard::TextEntryEnabled() const
{
return bTextEntryMode;
}

const std::string& Keyboard::GetCurrentText() const
{
return sTextEntryString;
}

int32_t Keyboard::GetTextCursorPosition() const
{
return nTextEntryCursorPos;
}

const std::vector<std::string>& Keyboard::GetTextCommands() const
{
return vecTextEntryCommands;
}

void Keyboard::ClearTextCommands()
{
vecTextEntryCommands.clear();
}

const Button& Keyboard::GetKey(const olc::Key key) const
{
return keys.at(size_t(key));
Expand Down Expand Up @@ -222,6 +262,55 @@ namespace olc::hw
if (state)
{
keyCache[nKeyCacheIndex].push_back(key);

// Process Text entry
if (bTextEntryMode)
{
const auto& sym = GetKeyGlyph(key, GetKey(olc::Key::SHIFT).bHeld, GetKey(olc::Key::CTRL).bHeld);


// Check for command characters
if (sym == "_L") // Left
nTextEntryCursorPos = std::max(0, nTextEntryCursorPos - 1);
else if (sym == "_R") // Right
nTextEntryCursorPos = std::min(int32_t(sTextEntryString.size()), nTextEntryCursorPos + 1);
else if (sym == "\b") // Backspace
{
// Still need 'back' to be consumed
if (nTextEntryCursorPos > 0)
{
sTextEntryString.erase(nTextEntryCursorPos - 1, 1);
nTextEntryCursorPos = std::max(0, nTextEntryCursorPos - 1);
}
}
else if (sym == "_X" && size_t(nTextEntryCursorPos) < sTextEntryString.size()) // Del
{
sTextEntryString.erase(nTextEntryCursorPos, 1);
}
/*else if (sym == "_U")
{

}
else if (sym == "_D")
{

}*/
else if (sym == sTextEntryDelim)
{
vecTextEntryCommands.push_back(sTextEntryString);
sTextEntryString.clear();
nTextEntryCursorPos = 0;
if(bLeaveTextEntryModeUponDelimiter)
DisableTextEntry();
}
else if (sym.size() == 1)
{
sTextEntryString.insert(nTextEntryCursorPos, sym);
nTextEntryCursorPos++;
}


}
}
}

Expand Down Expand Up @@ -253,6 +342,7 @@ namespace olc::hw

// Swap active cache index
nKeyCacheIndex ^= 0x01;

}
}
//! END IMPLEMENTATION
42 changes: 42 additions & 0 deletions dev/src/hw_keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>
#include <unordered_map>
#include <cstdint>
#include <algorithm>
#include <string>
//! END STDHEADER

Expand Down Expand Up @@ -85,6 +86,40 @@ namespace olc
// Set keyboard layout for glyph mapping
void UseKeyboardLayout(const olc::KeyboardLayout kbl);

public: // Text Entry Interface

// Text is handled differently to keyboard input, but is fundamentally
// driven by keyboard input (or at least events that smell like a keyboard)
//
// To use, EnableTextEntry() which then starts to accumulate keyboard
// input from OS. A cursor is also maintained that can be moved with
// left and right arrow keys, and delete and backspace work as expected.
//
// When the user presses a delimiter key (e.g. enter), the current text
// input is added to a command buffer the user can interrogate. This
// method is used because some 3rd party device drivers may leverage
// keyboard input to do other interesting things like barcode readers.
//
// Being in Text entry mode does not prohibit normal keyboard behaviour
// so endure your application handles keyboard input appropriately.
//
// Leaving TextEntryMode happens automatically when delimiter encountered

// Enter Text Entry Mode
void EnableTextEntry(const std::string_view sDefault = "", const std::string_view delim = "\n", const bool bLeaveMode = true);
// Leave Text Entry Mode
void DisableTextEntry();
// Is TextEntryMode enabled?
bool TextEntryEnabled() const;
// Get the current accumulated text input
const std::string& GetCurrentText() const;
// Get the current cursor position within the input string
int32_t GetTextCursorPosition() const;
// Get a RO vector of current commands
const std::vector<std::string>& GetTextCommands() const;
// User must clear accumulated commands when appropriate
void ClearTextCommands();

private:
// Set key state from system keycode
void SetKey(const olc::Key key, bool state);
Expand All @@ -104,6 +139,13 @@ namespace olc
std::array<std::vector<olc::Key>, 2> keyCache{};
size_t nKeyCacheIndex = 0;

bool bTextEntryMode = false;
std::string sTextEntryString = "";
int32_t nTextEntryCursorPos = 0;
std::vector<std::string> vecTextEntryCommands;
std::string sTextEntryDelim = "\n";
bool bLeaveTextEntryModeUponDelimiter = true;

// Map of olc::Keycodes to printable glyphs
struct sKeyGlyph
{
Expand Down
140 changes: 140 additions & 0 deletions examples/olcPGE3_TextEntry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
olc::PixelGameEngine3 Example - Text Entry Mode

Allows the user to enter and edit text

Licenced under the OLC-3 License
*/


// Define OLC_PGE3_APPLICATION to include the implementation of
// the Pixel Game Engine as part of this translation unit
#define OLC_PGE3_APPLICATION
#include "../olcPixelGameEngine3.h"

// Example application demonstrating text entry and editing. This class
// overrides the olc::PixelGameEngine base class by implementing
// the OnUserCreate() and OnUserUpdate() functions
class Example_TextEntryMode : public olc::PixelGameEngine
{
public:
Example_TextEntryMode()
{
sAppName = "Example - Text Entry Mode";
}

protected:
float fTickerTime = 0.0f;

// We'll draw random text in the background with
// a typing effect
std::list<std::string> listHistory;

public:
// Called once at the start, so create things here
bool OnUserCreate() override
{
listHistory.push_back("Welcome to Text Entry Demo!");
listHistory.push_back("");
listHistory.push_back("Press 'TAB' to enter text entry mode");
listHistory.push_back("Type your message");
listHistory.push_back("Move cursor with LEFT & RIGHT");
listHistory.push_back("Edit message with backspace and delete ");
listHistory.push_back("");
listHistory.push_back("Pressing 'ENTER' will force a 'command'");
listHistory.push_back("Give it a go!");
return true;
}

// Called every frame, so update things here
bool OnUserUpdate(float fElapsedTime) override
{
// Clear whole screen
draw.Clear(olc::Colour::VERY_DARK_BLUE);

// Check if commands from text entry mode exist
if (!keyboard.GetTextCommands().empty())
{
// There are commands waiting to be processed
for (const auto& sNewCommand : keyboard.GetTextCommands())
{
// capture them by adding to history queue
listHistory.push_back(sNewCommand);
if (listHistory.size() > 10)
listHistory.pop_front();
}

// Up to user to remove commands when appropriate
keyboard.ClearTextCommands();
}


// Draw Command History
olc::vf2d vPos = { 10,10 };
for (const auto& sCommand : listHistory)
{
draw.StringProp(vPos, sCommand, olc::Colour::CYAN);
vPos.y += 12;
}


if (keyboard.GetKey(olc::Key::TAB).bPressed)
{
if (keyboard.TextEntryEnabled())
{
keyboard.DisableTextEntry();
}
else
{
// Start text entry. In this instance we start with a
// blank input string, and we want to respond to the
// enter key being pressed. The false parameter tells
// the system not to leave text entry mode when a
// command has been entered, whcih for this demo is
// a lot more fluid.
keyboard.EnableTextEntry("", "\n", false);
}
}

if (keyboard.TextEntryEnabled())
{
// Read current text, and virtual cursor pos
const std::string& sText = keyboard.GetCurrentText();
const uint32_t nCursor = keyboard.GetTextCursorPosition();

// Work out where the cursor should be drawn
const auto vTextSizeToCursor = draw.GetTextSize(sText.substr(0, nCursor), true);

olc::vf2d vOffset = { 10,140 };

// Draw Cursor
draw.FilledRect(vOffset + olc::vf2d(0, -10) + vTextSizeToCursor, { 2, 10 }, olc::Colour::YELLOW);

// Draw Entered Text
draw.StringProp(vOffset, sText);

}


// Successful frame
return true;
}
};


// Main entry point for the application
int main()
{
// Construct demo application
Example_TextEntryMode demo;

// Create "screen" of 256x240 "pixels"
// with a pixel size of 4x4 actual screen pixels
if (demo.Construct({ 256, 240 }, { 4, 4 }))
{
// Start the application
demo.Start();
}

return 0;
}
Loading
Loading