diff --git a/src/interface/Component.cpp b/src/interface/Component.cpp index 7e229847e..3c52ed9dc 100644 --- a/src/interface/Component.cpp +++ b/src/interface/Component.cpp @@ -18,7 +18,8 @@ Component::Component(Window* parent_state): textPosition(0, 0), textSize(0, 0), iconPosition(0, 0), - drawn(false) + drawn(false), + menu(NULL) { } @@ -33,7 +34,8 @@ Component::Component(Point position, Point size): textPosition(0, 0), textSize(0, 0), iconPosition(0, 0), - drawn(false) + drawn(false), + menu(NULL) { } @@ -48,7 +50,8 @@ Component::Component(): textPosition(0, 0), textSize(0, 0), iconPosition(0, 0), - drawn(false) + drawn(false), + menu(NULL) { } @@ -154,6 +157,11 @@ void Component::SetParent(Panel* new_parent) // ***** OVERRIDEABLES ***** // Kept empty. +void Component::OnContextMenuAction(int item) +{ + +} + void Component::Draw(const Point& screenPos) { drawn = true; @@ -217,5 +225,6 @@ void Component::OnMouseWheelInside(int localx, int localy, int d) Component::~Component() { - + if(menu) + delete menu; } diff --git a/src/interface/Component.h b/src/interface/Component.h index 555be8436..bd3878ad4 100644 --- a/src/interface/Component.h +++ b/src/interface/Component.h @@ -7,6 +7,7 @@ namespace ui { + class ContextMenu; class Window; class Panel; @@ -25,6 +26,7 @@ namespace ui ui::Point textPosition; ui::Point textSize; ui::Point iconPosition; + ui::ContextMenu * menu; public: Component(Window* parent_state); Component(Point position, Point size); @@ -55,6 +57,8 @@ namespace ui //Get the parent component. inline Panel* const GetParent() const { return _parent; } + + virtual void OnContextMenuAction(int item); //UI functions: /* diff --git a/src/interface/ContextMenu.cpp b/src/interface/ContextMenu.cpp new file mode 100644 index 000000000..632b3143f --- /dev/null +++ b/src/interface/ContextMenu.cpp @@ -0,0 +1,86 @@ +#include "ContextMenu.h" + +using namespace ui; + +class ContextMenu::ItemSelectedAction: public ButtonAction +{ + ContextMenu * window; + int item; +public: + ItemSelectedAction(ContextMenu * window, int itemID): window(window), item(itemID) { } + virtual void ActionCallback(ui::Button *sender) + { + window->ActionCallback(sender, item); + } +}; + +ContextMenu::ContextMenu(Component * source): + Window(ui::Point(0, 0), ui::Point(0, 0)), + Appearance(source->Appearance), + source(source) +{ +} + +void ContextMenu::Show(ui::Point position) +{ + for(int i = 0; i < buttons.size(); i++) + { + RemoveComponent(buttons[i]); + delete buttons[i]; + } + buttons.clear(); + + Position = position; + Size.Y = items.size()*15; + Size.X = 100; + + int currentY = 1; + for(int i = 0; i < items.size(); i++) + { + Button * tempButton = new Button(Point(1, currentY), Point(Size.X-2, 14), items[i].Text); + tempButton->Appearance = Appearance; + tempButton->Enabled = items[i].Enabled; + tempButton->SetActionCallback(new ItemSelectedAction(this, items[i].ID)); + buttons.push_back(tempButton); + AddComponent(tempButton); + currentY += 15; + } + + ui::Engine::Ref().ShowWindow(this); +} + +void ContextMenu::ActionCallback(ui::Button *sender, int item) +{ + ui::Engine::Ref().CloseWindow(); + source->OnContextMenuAction(item); +} + +void ContextMenu::OnMouseDown(int x, int y, unsigned button) +{ + if(!(x > Position.X && y > Position.Y && y < Position.Y+Size.Y && x < Position.X+Size.X)) //Clicked outside window + ui::Engine::Ref().CloseWindow(); +} + +void ContextMenu::RemoveItem(int id) +{ + for(int i = 0; i < items.size(); i++) + { + if(items[i].ID == id) + { + items.erase(items.begin()+i); + break; + } + } +} + +void ContextMenu::AddItem(ContextMenuItem item) +{ + items.push_back(item); +} + +void ContextMenu::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 100, 100, 100, 255); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha); +} \ No newline at end of file diff --git a/src/interface/ContextMenu.h b/src/interface/ContextMenu.h new file mode 100644 index 000000000..fb804c365 --- /dev/null +++ b/src/interface/ContextMenu.h @@ -0,0 +1,39 @@ +#ifndef The_Powder_Toy_ContextMenu_h +#define The_Powder_Toy_ContextMenu_h + +#include "Window.h" +#include "Appearance.h" +#include "Button.h" + +namespace ui +{ + +class ContextMenuItem +{ +public: + int ID; + std::string Text; + bool Enabled; + ContextMenuItem(std::string text, int id, bool enabled) : Text(text), ID(id), Enabled(enabled) {} +}; + +class ContextMenu: public ui::Window, public ButtonAction { + std::vector buttons; + std::vector items; + bool isMouseInside; + ui::Component * source; +public: + ui::Appearance Appearance; + class ItemSelectedAction; + ContextMenu(Component * source); + virtual void ActionCallback(ui::Button *sender, int item); + virtual void AddItem(ContextMenuItem item); + virtual void RemoveItem(int id); + virtual void Show(ui::Point position); + virtual void OnDraw(); + virtual void OnMouseDown(int x, int y, unsigned button); + virtual ~ContextMenu() {} +}; +} + +#endif \ No newline at end of file diff --git a/src/interface/Label.cpp b/src/interface/Label.cpp index b7283c202..fd910865c 100644 --- a/src/interface/Label.cpp +++ b/src/interface/Label.cpp @@ -2,6 +2,7 @@ #include "Config.h" #include "Point.h" #include "Label.h" +#include "ContextMenu.h" using namespace ui; @@ -18,6 +19,8 @@ Label::Label(Point position, Point size, std::string labelText): autoHeight(size.Y==-1?true:false), caret(-1) { + menu = new ContextMenu(this); + menu->AddItem(ContextMenuItem("Copy", 0, true)); } Label::~Label() @@ -100,16 +103,52 @@ std::string Label::GetText() return this->text; } +void Label::OnContextMenuAction(int item) +{ + switch(item) + { + case 0: + copySelection(); + break; + } +} + void Label::OnMouseClick(int x, int y, unsigned button) { - selecting = true; - if(multiline) - selectionIndex0 = Graphics::CharIndexAtPosition((char*)textLines.c_str(), x-textPosition.X, y-textPosition.Y); + if(button == BUTTON_RIGHT) + { + if(menu) + menu->Show(GetParentWindow()->Position + Position + ui::Point(x, y)); + } else - selectionIndex0 = Graphics::CharIndexAtPosition((char*)text.c_str(), x-textPosition.X, y-textPosition.Y); - selectionIndex1 = selectionIndex0; + { + selecting = true; + if(multiline) + selectionIndex0 = Graphics::CharIndexAtPosition((char*)textLines.c_str(), x-textPosition.X, y-textPosition.Y); + else + selectionIndex0 = Graphics::CharIndexAtPosition((char*)text.c_str(), x-textPosition.X, y-textPosition.Y); + selectionIndex1 = selectionIndex0; - updateSelection(); + updateSelection(); + } +} + +void Label::copySelection() +{ + std::string currentText; + + if(multiline) + currentText = textLines; + else + currentText = text; + + if(selectionIndex1 > selectionIndex0) { + clipboard_push_text((char*)currentText.substr(selectionIndex0, selectionIndex1-selectionIndex0).c_str()); + } else if(selectionIndex0 > selectionIndex1) { + clipboard_push_text((char*)currentText.substr(selectionIndex1, selectionIndex0-selectionIndex1).c_str()); + } else { + clipboard_push_text((char*)currentText.c_str()); + } } void Label::OnMouseUp(int x, int y, unsigned button) @@ -117,6 +156,14 @@ void Label::OnMouseUp(int x, int y, unsigned button) selecting = false; } +void Label::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(ctrl && key == 'c') + { + copySelection(); + } +} + void Label::OnMouseMoved(int localx, int localy, int dx, int dy) { if(selecting) diff --git a/src/interface/Label.h b/src/interface/Label.h index c8c63d48c..1fa7148bb 100644 --- a/src/interface/Label.h +++ b/src/interface/Label.h @@ -37,6 +37,8 @@ namespace ui int getLowerSelectionBound(); int getHigherSelectionBound(); + + virtual void copySelection(); public: //Label(Window* parent_state, std::string labelText); Label(Point position, Point size, std::string labelText); @@ -53,9 +55,11 @@ namespace ui void SetTextColour(Colour textColour) { this->textColour = textColour; } + virtual void OnContextMenuAction(int item); virtual void OnMouseClick(int x, int y, unsigned button); virtual void OnMouseUp(int x, int y, unsigned button); virtual void OnMouseMoved(int localx, int localy, int dx, int dy); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); virtual void Draw(const Point& screenPos); virtual void Tick(float dt); }; diff --git a/src/interface/Textbox.cpp b/src/interface/Textbox.cpp index fbe3c6b21..54a9d4028 100644 --- a/src/interface/Textbox.cpp +++ b/src/interface/Textbox.cpp @@ -5,6 +5,7 @@ #include "interface/Point.h" #include "interface/Textbox.h" #include "interface/Keys.h" +#include "ContextMenu.h" using namespace ui; @@ -19,6 +20,11 @@ Textbox::Textbox(Point position, Point size, std::string textboxText, std::strin SetText(textboxText); cursor = text.length(); + + menu->RemoveItem(0); + menu->AddItem(ContextMenuItem("Cut", 1, true)); + menu->AddItem(ContextMenuItem("Copy", 0, true)); + menu->AddItem(ContextMenuItem("Paste", 2, true)); } Textbox::~Textbox() @@ -27,6 +33,18 @@ Textbox::~Textbox() delete actionCallback; } +void Textbox::SetHidden(bool hidden) +{ + menu->RemoveItem(0); + menu->RemoveItem(1); + menu->RemoveItem(2); + menu->AddItem(ContextMenuItem("Cut", 1, !hidden)); + menu->AddItem(ContextMenuItem("Copy", 0, !hidden)); + menu->AddItem(ContextMenuItem("Paste", 2, true)); + + masked = hidden; +} + void Textbox::SetPlaceholder(std::string text) { placeHolder = text; @@ -65,9 +83,134 @@ std::string Textbox::GetText() return backingText; } +void Textbox::OnContextMenuAction(int item) +{ + switch(item) + { + case 0: + copySelection(); + break; + case 1: + cutSelection(); + break; + case 2: + pasteIntoSelection(); + break; + } +} + +void Textbox::cutSelection() +{ + char * clipboardText; + clipboardText = clipboard_pull_text(); + std::string newText = std::string(clipboardText); + free(clipboardText); + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + clipboard_push_text((char*)backingText.substr(getLowerSelectionBound(), getHigherSelectionBound()-getLowerSelectionBound()).c_str()); + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + } + else + { + clipboard_push_text((char*)backingText.c_str()); + backingText.clear(); + } + ClearSelection(); + + if(masked) + { + std::string maskedText = std::string(backingText); + std::fill(maskedText.begin(), maskedText.end(), '\x8D'); + Label::SetText(maskedText); + } + else + { + text = backingText; + } + if(actionCallback) + actionCallback->TextChangedCallback(this); + + if(multiline) + updateMultiline(); + updateSelection(); + TextPosition(text); + + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } +} + +void Textbox::pasteIntoSelection() +{ + char * clipboardText; + clipboardText = clipboard_pull_text(); + std::string newText = std::string(clipboardText); + free(clipboardText); + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + } + backingText.insert(cursor, newText); + ClearSelection(); + + if(masked) + { + std::string maskedText = std::string(backingText); + std::fill(maskedText.begin(), maskedText.end(), '\x8D'); + Label::SetText(maskedText); + } + else + { + text = backingText; + } + if(actionCallback) + actionCallback->TextChangedCallback(this); + + if(multiline) + updateMultiline(); + updateSelection(); + TextPosition(text); + + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } +} + void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) { bool changed = false; + if(ctrl && key == 'c' && !masked) + { + copySelection(); + return; + } + if(ctrl && key == 'v') + { + pasteIntoSelection(); + return; + } + if(ctrl && key == 'x' && !masked) + { + cutSelection(); + return; + } + try { switch(key) @@ -190,15 +333,19 @@ void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool void Textbox::OnMouseClick(int x, int y, unsigned button) { - mouseDown = true; - cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), x-textPosition.X, y-textPosition.Y); - if(cursor) + + if(button != BUTTON_RIGHT) { - Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); - } - else - { - cursorPositionY = cursorPositionX = 0; + mouseDown = true; + cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), x-textPosition.X, y-textPosition.Y); + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } } Label::OnMouseClick(x, y, button); } diff --git a/src/interface/Textbox.h b/src/interface/Textbox.h index e8e9d221c..95588cb38 100644 --- a/src/interface/Textbox.h +++ b/src/interface/Textbox.h @@ -26,6 +26,9 @@ protected: TextboxAction *actionCallback; std::string backingText; std::string placeHolder; + + virtual void cutSelection(); + virtual void pasteIntoSelection(); public: Textbox(Point position, Point size, std::string textboxText = "", std::string textboxPlaceholder = ""); virtual ~Textbox(); @@ -37,10 +40,11 @@ public: virtual void SetPlaceholder(std::string text); void SetBorder(bool border) { this->border = border; }; - void SetHidden(bool hidden) { masked = hidden; } + void SetHidden(bool hidden); bool GetHidden() { return masked; } void SetActionCallback(TextboxAction * action) { actionCallback = action; } + virtual void OnContextMenuAction(int item); virtual void OnMouseClick(int x, int y, unsigned button); virtual void OnMouseUp(int x, int y, unsigned button); virtual void OnMouseMoved(int localx, int localy, int dx, int dy);