From 130db54051570a3eaa11deb64ca7971d40b0be76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 12 Jan 2014 13:54:03 +0100 Subject: [PATCH] HaikuDepot: Added work-in-progress TextEditor class The intention here is to be able to set it on a TextDocumentView to add editing capabilities. --- src/apps/haiku-depot/Jamfile | 2 + src/apps/haiku-depot/textview/TextEditor.cpp | 345 +++++++++++++++++++ src/apps/haiku-depot/textview/TextEditor.h | 85 +++++ 3 files changed, 432 insertions(+) create mode 100644 src/apps/haiku-depot/textview/TextEditor.cpp create mode 100644 src/apps/haiku-depot/textview/TextEditor.h diff --git a/src/apps/haiku-depot/Jamfile b/src/apps/haiku-depot/Jamfile index 602671e5f7..899aa608ce 100644 --- a/src/apps/haiku-depot/Jamfile +++ b/src/apps/haiku-depot/Jamfile @@ -26,6 +26,8 @@ local textDocumentSources = TextDocument.cpp TextDocumentLayout.cpp TextDocumentView.cpp + TextEditor.cpp + TextSelection.cpp TextSpan.cpp TextView.cpp ; diff --git a/src/apps/haiku-depot/textview/TextEditor.cpp b/src/apps/haiku-depot/textview/TextEditor.cpp new file mode 100644 index 0000000000..f3a28b35f7 --- /dev/null +++ b/src/apps/haiku-depot/textview/TextEditor.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2014, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "TextEditor.h" + +#include + + +TextEditor::TextEditor() + : + fDocument(), + fLayout(), + fSelection(), + fCaretAnchorX(0.0f), + fStyleAtCaret() +{ +} + + +TextEditor::TextEditor(const TextEditor& other) + : + fDocument(other.fDocument), + fLayout(other.fLayout), + fSelection(other.fSelection), + fCaretAnchorX(other.fCaretAnchorX), + fStyleAtCaret(other.fStyleAtCaret) +{ +} + + +TextEditor& +TextEditor::operator=(const TextEditor& other) +{ + if (this == &other) + return *this; + + fDocument = other.fDocument; + fLayout = other.fLayout; + fSelection = other.fSelection; + fCaretAnchorX = other.fCaretAnchorX; + fStyleAtCaret = other.fStyleAtCaret; + return *this; +} + + +bool +TextEditor::operator==(const TextEditor& other) const +{ + if (this == &other) + return true; + + return fDocument == other.fDocument + && fLayout == other.fLayout + && fSelection == other.fSelection + && fCaretAnchorX == other.fCaretAnchorX + && fStyleAtCaret == other.fStyleAtCaret; +} + + +bool +TextEditor::operator!=(const TextEditor& other) const +{ + return !(*this == other); +} + + +// #pragma mark - + + +void +TextEditor::SetDocument(const TextDocumentRef& ref) +{ + fDocument = ref; + SetSelection(TextSelection()); +} + + +void +TextEditor::SetLayout(const TextDocumentLayoutRef& ref) +{ + fLayout = ref; + SetSelection(TextSelection()); +} + + +void +TextEditor::SetSelection(TextSelection selection) +{ + _SetSelection(selection.Caret(), selection.Anchor(), true, true); +} + + +void +TextEditor::SetCharacterStyle(::CharacterStyle style) +{ + if (fStyleAtCaret == style) + return; + + fStyleAtCaret = style; + + if (fSelection.Caret() != fSelection.Anchor()) { + // TODO: Apply style to selection range + } +} + + +void +TextEditor::KeyDown(KeyEvent event) +{ + if (fDocument.Get() == NULL) + return; + + bool select = (event.modifiers & B_SHIFT_KEY) != 0; + + switch (event.key) { + case B_UP_ARROW: + _LineUp(select); + break; + + case B_DOWN_ARROW: + _LineDown(select); + break; + + case B_LEFT_ARROW: + if (_HasSelection() && !select) { + _SetCaretOffset( + std::min(fSelection.Caret(), fSelection.Anchor()), + true, false, true + ); + } else + _SetCaretOffset(fSelection.Caret() - 1, true, select, true); + break; + + case B_RIGHT_ARROW: + if (_HasSelection() && !select) { + _SetCaretOffset( + std::max(fSelection.Caret(), fSelection.Anchor()), + true, false, true + ); + } else + _SetCaretOffset(fSelection.Caret() + 1, true, select, true); + break; + + case B_HOME: + _LineStart(select); + break; + + case B_END: + _LineEnd(select); + break; + + case B_ENTER: + Insert(fSelection.Caret(), "\n"); + break; + + case B_TAB: + // TODO: Tab support in TextLayout + Insert(fSelection.Caret(), " "); + break; + + case B_ESCAPE: + break; + + case B_BACKSPACE: + if (_HasSelection()) { + Remove(_SelectionStart(), _SelectionLength()); + } else { + if (fSelection.Caret() > 0) + Remove(fSelection.Caret() - 1, 1); + } + break; + + case B_DELETE: + if (_HasSelection()) { + Remove(_SelectionStart(), _SelectionLength()); + } else { + if (fSelection.Caret() < fDocument->Length()) + Remove(fSelection.Caret(), 1); + } + break; + + case B_INSERT: + // TODO: Toggle insert mode (or maybe just don't support it) + break; + + case B_PAGE_UP: + case B_PAGE_DOWN: + case B_SUBSTITUTE: + case B_FUNCTION_KEY: + case B_KATAKANA_HIRAGANA: + case B_HANKAKU_ZENKAKU: + break; + + default: + if (event.bytes != NULL && event.length > 0) { + // Handle null-termintating the string + BString text(event.bytes, event.length); + Insert(fSelection.Caret(), text); + } + break; + } +} + + +void +TextEditor::Insert(int32 offset, const BString& string) +{ + // TODO: ... +} + + +void +TextEditor::Remove(int32 offset, int32 length) +{ + // TODO: ... +} + + +// #pragma mark - private + + +// _SetCaretOffset +void +TextEditor::_SetCaretOffset(int32 offset, bool updateAnchor, + bool lockSelectionAnchor, bool updateSelectionStyle) +{ + if (fDocument.Get() == NULL) + return; + + if (offset < 0) + offset = 0; + int32 textLength = fDocument->Length(); + if (offset > textLength) + offset = textLength; + + int32 caret = offset; + int32 anchor = lockSelectionAnchor ? fSelection.Anchor() : offset; + _SetSelection(caret, anchor, updateAnchor, updateSelectionStyle); +} + + +// _SetSelection +void +TextEditor::_SetSelection(int32 caret, int32 anchor, bool updateAnchor, + bool updateSelectionStyle) +{ + if (fLayout.Get() == NULL) + return; + + if (caret == fSelection.Caret() && caret == fSelection.Anchor()) + return; + + fSelection.SetAnchor(anchor); + fSelection.SetCaret(caret); + + if (updateAnchor) { + float x1; + float y1; + float x2; + float y2; + + fLayout->GetTextBounds(caret, x1, y1, x2, y2); + fCaretAnchorX = x1; + } + + if (updateSelectionStyle) + _UpdateStyleAtCaret(); +} + + +void +TextEditor::_UpdateStyleAtCaret() +{ + if (fDocument.Get() == NULL) + return; + + int32 offset = fSelection.Caret() - 1; + if (offset < 0) + offset = 0; + SetCharacterStyle(fDocument->CharacterStyleAt(offset)); +} + + +// #pragma mark - + + +void +TextEditor::_LineUp(bool select) +{ + // TODO +} + + +void +TextEditor::_LineDown(bool select) +{ + // TODO +} + + +void +TextEditor::_LineStart(bool select) +{ + // TODO +} + + +void +TextEditor::_LineEnd(bool select) +{ + // TODO +} + + +// #pragma mark - + + +bool +TextEditor::_HasSelection() const +{ + return _SelectionLength() > 0; +} + + +int32 +TextEditor::_SelectionStart() const +{ + return std::min(fSelection.Caret(), fSelection.Anchor()); +} + + +int32 +TextEditor::_SelectionEnd() const +{ + return std::max(fSelection.Caret(), fSelection.Anchor()); +} + + +int32 +TextEditor::_SelectionLength() const +{ + return _SelectionEnd() - _SelectionStart(); +} diff --git a/src/apps/haiku-depot/textview/TextEditor.h b/src/apps/haiku-depot/textview/TextEditor.h new file mode 100644 index 0000000000..c52c4b21c3 --- /dev/null +++ b/src/apps/haiku-depot/textview/TextEditor.h @@ -0,0 +1,85 @@ +/* + * Copyright 2014, Stephan Aßmus . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef TEXT_EDITOR_H +#define TEXT_EDITOR_H + + +#include "CharacterStyle.h" +#include "TextDocument.h" +#include "TextDocumentLayout.h" +#include "TextSelection.h" + + +class KeyEvent { +public: + const char* bytes; + int32 length; + int32 key; + int32 modifiers; +}; + + +class TextEditor { +public: + TextEditor(); + TextEditor(const TextEditor& other); + + TextEditor& operator=(const TextEditor& other); + bool operator==(const TextEditor& other) const; + bool operator!=(const TextEditor& other) const; + + void SetDocument(const TextDocumentRef& ref); + TextDocumentRef Document() const + { return fDocument; } + + void SetLayout( + const TextDocumentLayoutRef& ref); + TextDocumentLayoutRef Layout() const + { return fLayout; } + + void SetSelection(TextSelection selection); + inline TextSelection Selection() const + { return fSelection; } + + void SetCharacterStyle(::CharacterStyle style); + ::CharacterStyle CharacterStyle() const + { return fStyleAtCaret; } + + void KeyDown(KeyEvent event); + + void Insert(int32 offset, const BString& string); + void Remove(int32 offset, int32 length); + +private: + void _SetCaretOffset(int32 offset, + bool updateAnchor, + bool lockSelectionAnchor, + bool updateSelectionStyle); + void _SetSelection(int32 caret, int32 anchor, + bool updateAnchor, + bool updateSelectionStyle); + + void _UpdateStyleAtCaret(); + + void _LineUp(bool select); + void _LineDown(bool select); + void _LineStart(bool select); + void _LineEnd(bool select); + + bool _HasSelection() const; + int32 _SelectionStart() const; + int32 _SelectionEnd() const; + int32 _SelectionLength() const; + +private: + TextDocumentRef fDocument; + TextDocumentLayoutRef fLayout; + TextSelection fSelection; + float fCaretAnchorX; + ::CharacterStyle fStyleAtCaret; +}; + + +#endif // TEXT_EDITOR_H