Fleshed out more TextEditor functionality.

TextDocumentView now always has a TextEditor, but editing can be disabled.
The selection code lives only in TextEditor. Implemented more cursor
navigation code.
This commit is contained in:
Stephan Aßmus 2014-01-18 10:32:14 +01:00
parent 00d04bc7f2
commit 1bce247b22
4 changed files with 221 additions and 166 deletions

View File

@ -24,14 +24,14 @@ TextDocumentView::TextDocumentView(const char* name)
fInsetRight(0.0f),
fInsetBottom(0.0f),
fSelectionAnchorOffset(0),
fCaretOffset(0),
fCaretAnchorX(0.0f),
fCaretBounds(),
fShowCaret(false),
fMouseDown(false)
{
fTextDocumentLayout.SetWidth(_TextLayoutWidth(Bounds().Width()));
// Set default TextEditor
SetTextEditor(TextEditorRef(new(std::nothrow) TextEditor(), true));
SetViewColor(B_TRANSPARENT_COLOR);
SetLowColor(255, 255, 255, 255);
@ -67,11 +67,14 @@ TextDocumentView::Draw(BRect updateRect)
fTextDocumentLayout.SetWidth(_TextLayoutWidth(Bounds().Width()));
fTextDocumentLayout.Draw(this, BPoint(fInsetLeft, fInsetTop), updateRect);
bool isCaret = fSelectionAnchorOffset == fCaretOffset;
if (fTextEditor.Get() == NULL)
return;
bool isCaret = fTextEditor->SelectionLength() == 0;
if (isCaret) {
if (fShowCaret && fTextEditor.Get() != NULL)
_DrawCaret(fCaretOffset);
if (fShowCaret && fTextEditor->IsEditingEnabled())
_DrawCaret(fTextEditor->CaretOffset());
} else {
_DrawSelection();
}
@ -174,11 +177,13 @@ TextDocumentView::KeyDown(const char* bytes, int32 numBytes)
if (Window() != NULL && Window()->CurrentMessage() != NULL) {
BMessage* message = Window()->CurrentMessage();
message->FindInt32("key", &event.key);
message->FindInt32("raw_char", &event.key);
message->FindInt32("modifiers", &event.modifiers);
}
fTextEditor->KeyDown(event);
fShowCaret = true;
Invalidate();
}
@ -245,16 +250,20 @@ TextDocumentView::SetTextDocument(const TextDocumentRef& document)
if (fTextEditor.Get() != NULL)
fTextEditor->SetDocument(document);
fSelectionAnchorOffset = 0;
fCaretOffset = 0;
fCaretAnchorX = 0.0f;
InvalidateLayout();
Invalidate();
_UpdateScrollBars();
}
void
TextDocumentView::SetEditingEnabled(bool enabled)
{
if (fTextEditor.Get() != NULL)
fTextEditor->SetEditingEnabled(enabled);
}
void
TextDocumentView::SetTextEditor(const TextEditorRef& editor)
{
@ -262,6 +271,8 @@ TextDocumentView::SetTextEditor(const TextEditorRef& editor)
return;
if (fTextEditor.Get() != NULL) {
fTextEditor->SetDocument(TextDocumentRef());
fTextEditor->SetLayout(TextDocumentLayoutRef());
// TODO: Probably has to remove listeners
}
@ -269,6 +280,8 @@ TextDocumentView::SetTextEditor(const TextEditorRef& editor)
if (fTextEditor.Get() != NULL) {
fTextEditor->SetDocument(fTextDocument);
fTextEditor->SetLayout(TextDocumentLayoutRef(
&fTextDocumentLayout));
// TODO: Probably has to add listeners
}
}
@ -307,38 +320,33 @@ TextDocumentView::SetInsets(float left, float top, float right, float bottom)
void
TextDocumentView::SetCaret(const BPoint& location, bool extendSelection)
TextDocumentView::SetCaret(BPoint location, bool extendSelection)
{
if (fTextDocument.Get() == NULL)
if (fTextEditor.Get() == NULL)
return;
bool rightOfChar = false;
int32 caretOffset = fTextDocumentLayout.TextOffsetAt(
location.x - fInsetLeft, location.y - fInsetTop, rightOfChar);
location.x -= fInsetLeft;
location.y -= fInsetTop;
if (rightOfChar)
caretOffset++;
_SetCaretOffset(caretOffset, true, extendSelection);
fTextEditor->SetCaret(location, extendSelection);
fShowCaret = !extendSelection;
Invalidate();
}
bool
TextDocumentView::HasSelection() const
{
return fSelectionAnchorOffset != fCaretOffset;
return fTextEditor.Get() != NULL && fTextEditor->HasSelection();
}
void
TextDocumentView::GetSelection(int32& start, int32& end) const
{
if (fSelectionAnchorOffset <= fCaretOffset) {
start = fSelectionAnchorOffset;
end = fCaretOffset;
} else {
start = fCaretOffset;
end = fSelectionAnchorOffset;
if (fTextEditor.Get()) {
start = fTextEditor->SelectionStart();
end = fTextEditor->SelectionEnd();
}
}
@ -346,8 +354,7 @@ TextDocumentView::GetSelection(int32& start, int32& end) const
void
TextDocumentView::Copy(BClipboard* clipboard)
{
if (fSelectionAnchorOffset == fCaretOffset
|| fTextDocument.Get() == NULL) {
if (!HasSelection() || fTextDocument.Get() == NULL) {
// Nothing to copy, don't clear clipboard contents for now reason.
return;
}
@ -425,41 +432,6 @@ TextDocumentView::_UpdateScrollBars()
}
void
TextDocumentView::_SetCaretOffset(int32 offset, bool updateAnchor,
bool lockSelectionAnchor)
{
if (offset < 0)
offset = 0;
int32 length = fTextDocument->Length();
if (offset > length)
offset = length;
if (offset == fCaretOffset && (lockSelectionAnchor
|| offset == fSelectionAnchorOffset)) {
return;
}
if (!lockSelectionAnchor)
fSelectionAnchorOffset = offset;
fCaretOffset = offset;
fShowCaret = true;
if (updateAnchor) {
float x1;
float y1;
float x2;
float y2;
fTextDocumentLayout.GetTextBounds(fCaretOffset, x1, y1, x2, y2);
fCaretAnchorX = x1;
}
Invalidate();
}
void
TextDocumentView::_DrawCaret(int32 textOffset)
{

View File

@ -52,6 +52,7 @@ public:
void SetTextDocument(
const TextDocumentRef& document);
void SetEditingEnabled(bool enabled);
void SetTextEditor(
const TextEditorRef& editor);
@ -60,8 +61,7 @@ public:
void SetInsets(float left, float top, float right,
float bottom);
void SetCaret(const BPoint& where,
bool extendSelection);
void SetCaret(BPoint where, bool extendSelection);
bool HasSelection() const;
void GetSelection(int32& start, int32& end) const;
@ -73,9 +73,6 @@ private:
void _UpdateScrollBars();
void _SetCaretOffset(int32 offset, bool updateAnchor,
bool lockSelectionAnchor);
void _DrawCaret(int32 textOffset);
void _DrawSelection();
void _GetSelectionShape(BShape& shape,
@ -91,12 +88,8 @@ private:
float fInsetRight;
float fInsetBottom;
int32 fSelectionAnchorOffset;
int32 fCaretOffset;
float fCaretAnchorX;
bool fShowCaret;
BRect fCaretBounds;
bool fShowCaret;
bool fMouseDown;
};

View File

@ -14,7 +14,8 @@ TextEditor::TextEditor()
fLayout(),
fSelection(),
fCaretAnchorX(0.0f),
fStyleAtCaret()
fStyleAtCaret(),
fEditingEnabled(true)
{
}
@ -25,7 +26,13 @@ TextEditor::TextEditor(const TextEditor& other)
fLayout(other.fLayout),
fSelection(other.fSelection),
fCaretAnchorX(other.fCaretAnchorX),
fStyleAtCaret(other.fStyleAtCaret)
fStyleAtCaret(other.fStyleAtCaret),
fEditingEnabled(other.fEditingEnabled)
{
}
TextEditor::~TextEditor()
{
}
@ -41,6 +48,7 @@ TextEditor::operator=(const TextEditor& other)
fSelection = other.fSelection;
fCaretAnchorX = other.fCaretAnchorX;
fStyleAtCaret = other.fStyleAtCaret;
fEditingEnabled = other.fEditingEnabled;
return *this;
}
@ -55,7 +63,8 @@ TextEditor::operator==(const TextEditor& other) const
&& fLayout == other.fLayout
&& fSelection == other.fSelection
&& fCaretAnchorX == other.fCaretAnchorX
&& fStyleAtCaret == other.fStyleAtCaret;
&& fStyleAtCaret == other.fStyleAtCaret
&& fEditingEnabled == other.fEditingEnabled;
}
@ -85,6 +94,30 @@ TextEditor::SetLayout(const TextDocumentLayoutRef& ref)
}
void
TextEditor::SetEditingEnabled(bool enabled)
{
fEditingEnabled = enabled;
}
void
TextEditor::SetCaret(BPoint location, bool extendSelection)
{
if (fDocument.Get() == NULL || fLayout.Get() == NULL)
return;
bool rightOfChar = false;
int32 caretOffset = fLayout->TextOffsetAt(location.x, location.y,
rightOfChar);
if (rightOfChar)
caretOffset++;
_SetCaretOffset(caretOffset, true, extendSelection, true);
}
void
TextEditor::SetSelection(TextSelection selection)
{
@ -116,15 +149,15 @@ TextEditor::KeyDown(KeyEvent event)
switch (event.key) {
case B_UP_ARROW:
_LineUp(select);
LineUp(select);
break;
case B_DOWN_ARROW:
_LineDown(select);
LineDown(select);
break;
case B_LEFT_ARROW:
if (_HasSelection() && !select) {
if (HasSelection() && !select) {
_SetCaretOffset(
std::min(fSelection.Caret(), fSelection.Anchor()),
true, false, true
@ -134,7 +167,7 @@ TextEditor::KeyDown(KeyEvent event)
break;
case B_RIGHT_ARROW:
if (_HasSelection() && !select) {
if (HasSelection() && !select) {
_SetCaretOffset(
std::max(fSelection.Caret(), fSelection.Anchor()),
true, false, true
@ -144,11 +177,11 @@ TextEditor::KeyDown(KeyEvent event)
break;
case B_HOME:
_LineStart(select);
LineStart(select);
break;
case B_END:
_LineEnd(select);
LineEnd(select);
break;
case B_ENTER:
@ -164,8 +197,8 @@ TextEditor::KeyDown(KeyEvent event)
break;
case B_BACKSPACE:
if (_HasSelection()) {
Remove(_SelectionStart(), _SelectionLength());
if (HasSelection()) {
Remove(SelectionStart(), SelectionLength());
} else {
if (fSelection.Caret() > 0)
Remove(fSelection.Caret() - 1, 1);
@ -173,8 +206,8 @@ TextEditor::KeyDown(KeyEvent event)
break;
case B_DELETE:
if (_HasSelection()) {
Remove(_SelectionStart(), _SelectionLength());
if (HasSelection()) {
Remove(SelectionStart(), SelectionLength());
} else {
if (fSelection.Caret() < fDocument->Length())
Remove(fSelection.Caret(), 1);
@ -207,6 +240,9 @@ TextEditor::KeyDown(KeyEvent event)
void
TextEditor::Insert(int32 offset, const BString& string)
{
if (!fEditingEnabled)
return;
// TODO: ...
}
@ -214,14 +250,119 @@ TextEditor::Insert(int32 offset, const BString& string)
void
TextEditor::Remove(int32 offset, int32 length)
{
if (!fEditingEnabled)
return;
// TODO: ...
}
// #pragma mark -
void
TextEditor::LineUp(bool select)
{
if (fLayout.Get() == NULL)
return;
int32 lineIndex = fLayout->LineIndexForOffset(fSelection.Caret());
_MoveToLine(lineIndex - 1, select);
}
void
TextEditor::LineDown(bool select)
{
if (fLayout.Get() == NULL)
return;
int32 lineIndex = fLayout->LineIndexForOffset(fSelection.Caret());
_MoveToLine(lineIndex + 1, select);
}
void
TextEditor::LineStart(bool select)
{
if (fLayout.Get() == NULL)
return;
int32 lineIndex = fLayout->LineIndexForOffset(fSelection.Caret());
_SetCaretOffset(fLayout->FirstOffsetOnLine(lineIndex), true, select,
true);
}
void
TextEditor::LineEnd(bool select)
{
if (fLayout.Get() == NULL)
return;
int32 lineIndex = fLayout->LineIndexForOffset(fSelection.Caret());
_SetCaretOffset(fLayout->LastOffsetOnLine(lineIndex), true, select,
true);
}
// #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();
}
// #pragma mark - private
// _SetCaretOffset
// _MoveToLine
void
TextEditor::_MoveToLine(int32 lineIndex, bool select)
{
if (lineIndex < 0 || lineIndex >= fLayout->CountLines())
return;
float x1;
float y1;
float x2;
float y2;
fLayout->GetLineBounds(lineIndex , x1, y1, x2, y2);
bool rightOfCenter;
int32 textOffset = fLayout->TextOffsetAt(fCaretAnchorX, (y1 + y2) / 2,
rightOfCenter);
if (rightOfCenter)
textOffset++;
_SetCaretOffset(textOffset, false, select, true);
}
void
TextEditor::_SetCaretOffset(int32 offset, bool updateAnchor,
bool lockSelectionAnchor, bool updateSelectionStyle)
@ -241,7 +382,6 @@ TextEditor::_SetCaretOffset(int32 offset, bool updateAnchor,
}
// _SetSelection
void
TextEditor::_SetSelection(int32 caret, int32 anchor, bool updateAnchor,
bool updateSelectionStyle)
@ -249,11 +389,11 @@ TextEditor::_SetSelection(int32 caret, int32 anchor, bool updateAnchor,
if (fLayout.Get() == NULL)
return;
if (caret == fSelection.Caret() && caret == fSelection.Anchor())
if (caret == fSelection.Caret() && anchor == fSelection.Anchor())
return;
fSelection.SetAnchor(anchor);
fSelection.SetCaret(caret);
fSelection.SetAnchor(anchor);
if (updateAnchor) {
float x1;
@ -283,63 +423,3 @@ TextEditor::_UpdateStyleAtCaret()
}
// #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();
}

View File

@ -6,6 +6,7 @@
#define TEXT_EDITOR_H
#include <Point.h>
#include <Referenceable.h>
#include "CharacterStyle.h"
@ -27,6 +28,7 @@ class TextEditor : public BReferenceable {
public:
TextEditor();
TextEditor(const TextEditor& other);
virtual ~TextEditor();
TextEditor& operator=(const TextEditor& other);
bool operator==(const TextEditor& other) const;
@ -41,6 +43,11 @@ public:
TextDocumentLayoutRef Layout() const
{ return fLayout; }
void SetEditingEnabled(bool enabled);
inline bool IsEditingEnabled() const
{ return fEditingEnabled; }
void SetCaret(BPoint location, bool extendSelection);
void SetSelection(TextSelection selection);
inline TextSelection Selection() const
{ return fSelection; }
@ -49,12 +56,25 @@ public:
::CharacterStyle CharacterStyle() const
{ return fStyleAtCaret; }
void KeyDown(KeyEvent event);
virtual void KeyDown(KeyEvent event);
void Insert(int32 offset, const BString& string);
void Remove(int32 offset, int32 length);
virtual void Insert(int32 offset, const BString& string);
virtual void Remove(int32 offset, int32 length);
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;
inline int32 CaretOffset() const
{ return fSelection.Caret(); }
private:
void _MoveToLine(int32 lineIndex, bool select);
void _SetCaretOffset(int32 offset,
bool updateAnchor,
bool lockSelectionAnchor,
@ -64,23 +84,13 @@ private:
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;
bool fEditingEnabled;
};