HaikuDepot: Text stuff: Support for simple bullet lists.
Also: * Fixed off-by-one bug when extracting wrapped TextSpans until the line's end text offset. * ParagraphStyle::FirstLineInset() is now additional to LineInset(). * Potential Bullet::Spacing() is added to all lines as inset.
This commit is contained in:
@@ -13,6 +13,8 @@ for sourceDir in $(sourceDirs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local textDocumentSources =
|
local textDocumentSources =
|
||||||
|
Bullet.cpp
|
||||||
|
BulletData.cpp
|
||||||
CharacterStyle.cpp
|
CharacterStyle.cpp
|
||||||
CharacterStyleData.cpp
|
CharacterStyleData.cpp
|
||||||
Paragraph.cpp
|
Paragraph.cpp
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Stephan Aßmus <[email protected]>.
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Bullet.h"
|
||||||
|
|
||||||
|
|
||||||
|
static BulletData sEmptyBullet;
|
||||||
|
|
||||||
|
|
||||||
|
Bullet::Bullet()
|
||||||
|
:
|
||||||
|
fBulletData(&sEmptyBullet)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bullet::Bullet(const BString& string, float spacing)
|
||||||
|
:
|
||||||
|
fBulletData(new BulletData(string, spacing), true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bullet::Bullet(const Bullet& other)
|
||||||
|
:
|
||||||
|
fBulletData(other.fBulletData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bullet&
|
||||||
|
Bullet::operator=(const Bullet& other)
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
fBulletData = other.fBulletData;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Bullet::operator==(const Bullet& other) const
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (fBulletData == other.fBulletData)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (fBulletData.Get() != NULL && other.fBulletData.Get() != NULL)
|
||||||
|
return *fBulletData.Get() == *other.fBulletData.Get();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Bullet::operator!=(const Bullet& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Bullet::SetString(const BString& string)
|
||||||
|
{
|
||||||
|
BulletDataRef data = fBulletData->SetString(string);
|
||||||
|
if (data == fBulletData)
|
||||||
|
return data->String() == string;
|
||||||
|
|
||||||
|
fBulletData = data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const BString&
|
||||||
|
Bullet::String() const
|
||||||
|
{
|
||||||
|
return fBulletData->String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Bullet::SetSpacing(float spacing)
|
||||||
|
{
|
||||||
|
BulletDataRef data = fBulletData->SetSpacing(spacing);
|
||||||
|
if (data == fBulletData)
|
||||||
|
return data->Spacing() == spacing;
|
||||||
|
|
||||||
|
fBulletData = data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float
|
||||||
|
Bullet::Spacing() const
|
||||||
|
{
|
||||||
|
return fBulletData->Spacing();
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Stephan Aßmus <[email protected]>.
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef BULLET_H
|
||||||
|
#define BULLET_H
|
||||||
|
|
||||||
|
#include "BulletData.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Bullet {
|
||||||
|
public:
|
||||||
|
Bullet();
|
||||||
|
Bullet(const BString& string, float spacing);
|
||||||
|
Bullet(const Bullet& other);
|
||||||
|
|
||||||
|
Bullet& operator=(const Bullet& other);
|
||||||
|
bool operator==(const Bullet& other) const;
|
||||||
|
bool operator!=(const Bullet& other) const;
|
||||||
|
|
||||||
|
bool SetString(const BString& string);
|
||||||
|
const BString& String() const;
|
||||||
|
|
||||||
|
bool SetSpacing(float spacing);
|
||||||
|
float Spacing() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BulletDataRef fBulletData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BULLET_H
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Stephan Aßmus <[email protected]>.
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BulletData.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
|
||||||
|
BulletData::BulletData()
|
||||||
|
:
|
||||||
|
fString(""),
|
||||||
|
fSpacing(0.0f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BulletData::BulletData(const BString& string, float spacing)
|
||||||
|
:
|
||||||
|
fString(string),
|
||||||
|
fSpacing(spacing)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BulletData::BulletData(const BulletData& other)
|
||||||
|
:
|
||||||
|
fString(other.fString),
|
||||||
|
fSpacing(other.fSpacing)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BulletData::operator==(const BulletData& other) const
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return fString == other.fString
|
||||||
|
&& fSpacing == other.fSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BulletData::operator!=(const BulletData& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BulletDataRef
|
||||||
|
BulletData::SetString(const BString& string)
|
||||||
|
{
|
||||||
|
if (fString == string)
|
||||||
|
return BulletDataRef(this);
|
||||||
|
|
||||||
|
BulletData* ret = new(std::nothrow) BulletData(*this);
|
||||||
|
if (ret == NULL)
|
||||||
|
return BulletDataRef(this);
|
||||||
|
|
||||||
|
ret->fString = string;
|
||||||
|
return BulletDataRef(ret, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BulletDataRef
|
||||||
|
BulletData::SetSpacing(float spacing)
|
||||||
|
{
|
||||||
|
if (fSpacing == spacing)
|
||||||
|
return BulletDataRef(this);
|
||||||
|
|
||||||
|
BulletData* ret = new(std::nothrow) BulletData(*this);
|
||||||
|
if (ret == NULL)
|
||||||
|
return BulletDataRef(this);
|
||||||
|
|
||||||
|
ret->fSpacing = spacing;
|
||||||
|
return BulletDataRef(ret, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - private
|
||||||
|
|
||||||
|
|
||||||
|
BulletData&
|
||||||
|
BulletData::operator=(const BulletData& other)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013, Stephan Aßmus <[email protected]>.
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef BULLET_DATA_H
|
||||||
|
#define BULLET_DATA_H
|
||||||
|
|
||||||
|
#include <Referenceable.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BulletData;
|
||||||
|
typedef BReference<BulletData> BulletDataRef;
|
||||||
|
|
||||||
|
|
||||||
|
// You cannot modify a BulletData object once it has been created.
|
||||||
|
class BulletData : public BReferenceable {
|
||||||
|
public:
|
||||||
|
BulletData();
|
||||||
|
BulletData(const BString& string,
|
||||||
|
float spacing);
|
||||||
|
BulletData(const BulletData& other);
|
||||||
|
|
||||||
|
bool operator==(
|
||||||
|
const BulletData& other) const;
|
||||||
|
bool operator!=(
|
||||||
|
const BulletData& other) const;
|
||||||
|
|
||||||
|
BulletDataRef SetString(const BString& string);
|
||||||
|
inline const BString& String() const
|
||||||
|
{ return fString; }
|
||||||
|
|
||||||
|
BulletDataRef SetSpacing(float spacing);
|
||||||
|
inline float Spacing() const
|
||||||
|
{ return fSpacing; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BulletData& operator=(const BulletData& other);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BString fString;
|
||||||
|
float fSpacing;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BULLET_DATA_H
|
||||||
@@ -257,6 +257,17 @@ ParagraphLayout::Draw(BView* view, const BPoint& offset)
|
|||||||
const LineInfo& line = fLineInfos.ItemAtFast(i);
|
const LineInfo& line = fLineInfos.ItemAtFast(i);
|
||||||
_DrawLine(view, offset, line);
|
_DrawLine(view, offset, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Bullet& bullet = fParagraphStyle.Bullet();
|
||||||
|
if (bullet.Spacing() > 0.0f && bullet.String().Length() > 0) {
|
||||||
|
// Draw bullet at offset
|
||||||
|
view->SetHighColor(0, 0, 0, 255);
|
||||||
|
BPoint bulletPos(offset);
|
||||||
|
bulletPos.x += fParagraphStyle.FirstLineInset()
|
||||||
|
+ fParagraphStyle.LineInset();
|
||||||
|
bulletPos.y += fLineInfos.ItemAt(0).maxAscent;
|
||||||
|
view->DrawString(bullet.String(), bulletPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -295,7 +306,10 @@ ParagraphLayout::_Layout()
|
|||||||
{
|
{
|
||||||
fLineInfos.Clear();
|
fLineInfos.Clear();
|
||||||
|
|
||||||
float x = fParagraphStyle.FirstLineInset();
|
const Bullet& bullet = fParagraphStyle.Bullet();
|
||||||
|
|
||||||
|
float x = fParagraphStyle.LineInset() + fParagraphStyle.FirstLineInset()
|
||||||
|
+ bullet.Spacing();
|
||||||
float y = 0.0f;
|
float y = 0.0f;
|
||||||
int lineIndex = 0;
|
int lineIndex = 0;
|
||||||
int lineStart = 0;
|
int lineStart = 0;
|
||||||
@@ -388,7 +402,7 @@ ParagraphLayout::_Layout()
|
|||||||
_FinalizeLine(lineStart, lineEnd, lineIndex, y, lineHeight);
|
_FinalizeLine(lineStart, lineEnd, lineIndex, y, lineHeight);
|
||||||
|
|
||||||
// Start position of the next line
|
// Start position of the next line
|
||||||
x = fParagraphStyle.LineInset();
|
x = fParagraphStyle.LineInset() + bullet.Spacing();
|
||||||
y += lineHeight + fParagraphStyle.LineSpacing();
|
y += lineHeight + fParagraphStyle.LineSpacing();
|
||||||
|
|
||||||
if (lineBreak)
|
if (lineBreak)
|
||||||
@@ -642,7 +656,7 @@ ParagraphLayout::_FinalizeLine(int lineStart, int lineEnd, int lineIndex,
|
|||||||
if (addSpan) {
|
if (addSpan) {
|
||||||
const TextSpan& span = fTextSpans.ItemAt(spanIndex);
|
const TextSpan& span = fTextSpans.ItemAt(spanIndex);
|
||||||
TextSpan subSpan = span.SubSpan(i - spanStart,
|
TextSpan subSpan = span.SubSpan(i - spanStart,
|
||||||
(lineEnd - spanStart) - (i - spanStart));
|
(lineEnd - spanStart + 1) - (i - spanStart));
|
||||||
line.layoutedSpans.Add(subSpan);
|
line.layoutedSpans.Add(subSpan);
|
||||||
_IncludeStyleInLine(line, span.Style());
|
_IncludeStyleInLine(line, span.Style());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,3 +187,22 @@ ParagraphStyle::SpacingBottom() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
ParagraphStyle::SetBullet(const ::Bullet& bullet)
|
||||||
|
{
|
||||||
|
ParagraphStyleDataRef data = fStyleData->SetBullet(bullet);
|
||||||
|
if (data == fStyleData)
|
||||||
|
return data->Bullet() == bullet;
|
||||||
|
|
||||||
|
fStyleData = data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ::Bullet&
|
||||||
|
ParagraphStyle::Bullet() const
|
||||||
|
{
|
||||||
|
return fStyleData->Bullet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ public:
|
|||||||
bool SetSpacingBottom(float spacing);
|
bool SetSpacingBottom(float spacing);
|
||||||
float SpacingBottom() const;
|
float SpacingBottom() const;
|
||||||
|
|
||||||
|
bool SetBullet(const ::Bullet& bullet);
|
||||||
|
const ::Bullet& Bullet() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ParagraphStyleDataRef fStyleData;
|
ParagraphStyleDataRef fStyleData;
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ ParagraphStyleData::ParagraphStyleData()
|
|||||||
fLineInset(0.0f),
|
fLineInset(0.0f),
|
||||||
|
|
||||||
fSpacingTop(0.0f),
|
fSpacingTop(0.0f),
|
||||||
fSpacingBottom(0.0f)
|
fSpacingBottom(0.0f),
|
||||||
|
|
||||||
|
fBullet()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +33,9 @@ ParagraphStyleData::ParagraphStyleData(const ParagraphStyleData& other)
|
|||||||
fLineInset(other.fLineInset),
|
fLineInset(other.fLineInset),
|
||||||
|
|
||||||
fSpacingTop(other.fSpacingTop),
|
fSpacingTop(other.fSpacingTop),
|
||||||
fSpacingBottom(other.fSpacingBottom)
|
fSpacingBottom(other.fSpacingBottom),
|
||||||
|
|
||||||
|
fBullet(other.fBullet)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +51,8 @@ ParagraphStyleData::operator==(const ParagraphStyleData& other) const
|
|||||||
&& fFirstLineInset == other.fFirstLineInset
|
&& fFirstLineInset == other.fFirstLineInset
|
||||||
&& fLineInset == other.fLineInset
|
&& fLineInset == other.fLineInset
|
||||||
&& fSpacingTop == other.fSpacingTop
|
&& fSpacingTop == other.fSpacingTop
|
||||||
&& fSpacingBottom == other.fSpacingBottom;
|
&& fSpacingBottom == other.fSpacingBottom
|
||||||
|
&& fBullet == other.fBullet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -163,6 +168,21 @@ ParagraphStyleData::SetSpacingBottom(float spacing)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ParagraphStyleDataRef
|
||||||
|
ParagraphStyleData::SetBullet(const ::Bullet& bullet)
|
||||||
|
{
|
||||||
|
if (fBullet == bullet)
|
||||||
|
return ParagraphStyleDataRef(this);
|
||||||
|
|
||||||
|
ParagraphStyleData* ret = new(std::nothrow) ParagraphStyleData(*this);
|
||||||
|
if (ret == NULL)
|
||||||
|
return ParagraphStyleDataRef(this);
|
||||||
|
|
||||||
|
ret->fBullet = bullet;
|
||||||
|
return ParagraphStyleDataRef(ret, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - private
|
// #pragma mark - private
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#ifndef PARAGRAPH_STYLE_DATA_H
|
#ifndef PARAGRAPH_STYLE_DATA_H
|
||||||
#define PARAGRAPH_STYLE_DATA_H
|
#define PARAGRAPH_STYLE_DATA_H
|
||||||
|
|
||||||
#include <Referenceable.h>
|
#include "Bullet.h"
|
||||||
|
|
||||||
|
|
||||||
enum Alignment {
|
enum Alignment {
|
||||||
@@ -60,6 +60,9 @@ public:
|
|||||||
inline float SpacingBottom() const
|
inline float SpacingBottom() const
|
||||||
{ return fSpacingBottom; }
|
{ return fSpacingBottom; }
|
||||||
|
|
||||||
|
ParagraphStyleDataRef SetBullet(const ::Bullet& bullet);
|
||||||
|
inline const ::Bullet& Bullet() const
|
||||||
|
{ return fBullet; }
|
||||||
private:
|
private:
|
||||||
ParagraphStyleData& operator=(const ParagraphStyleData& other);
|
ParagraphStyleData& operator=(const ParagraphStyleData& other);
|
||||||
|
|
||||||
@@ -73,6 +76,8 @@ private:
|
|||||||
|
|
||||||
float fSpacingTop;
|
float fSpacingTop;
|
||||||
float fSpacingBottom;
|
float fSpacingBottom;
|
||||||
|
|
||||||
|
::Bullet fBullet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ TextDocumentTest::ReadyToRun()
|
|||||||
TextDocumentRef document(new TextDocument(), true);
|
TextDocumentRef document(new TextDocument(), true);
|
||||||
|
|
||||||
Paragraph paragraph(paragraphStyle);
|
Paragraph paragraph(paragraphStyle);
|
||||||
paragraph.Append(TextSpan("This is a ", regularStyle));
|
paragraph.Append(TextSpan("This is a", regularStyle));
|
||||||
paragraph.Append(TextSpan("test", bigStyle));
|
paragraph.Append(TextSpan(" test ", bigStyle));
|
||||||
paragraph.Append(TextSpan(" to see if ", regularStyle));
|
paragraph.Append(TextSpan("to see if ", regularStyle));
|
||||||
paragraph.Append(TextSpan("different", boldStyle));
|
paragraph.Append(TextSpan("different", boldStyle));
|
||||||
paragraph.Append(TextSpan(" character styles already work.", regularStyle));
|
paragraph.Append(TextSpan(" character styles already work.", regularStyle));
|
||||||
document->Append(paragraph);
|
document->Append(paragraph);
|
||||||
@@ -70,6 +70,22 @@ TextDocumentTest::ReadyToRun()
|
|||||||
paragraph.Append(TextSpan(" supported as of now!", regularStyle));
|
paragraph.Append(TextSpan(" supported as of now!", regularStyle));
|
||||||
document->Append(paragraph);
|
document->Append(paragraph);
|
||||||
|
|
||||||
|
// Test a bullet list
|
||||||
|
paragraphStyle.SetSpacingTop(8.0f);
|
||||||
|
paragraphStyle.SetAlignment(ALIGN_LEFT);
|
||||||
|
paragraphStyle.SetJustify(true);
|
||||||
|
paragraphStyle.SetBullet(Bullet("•", 12.0f));
|
||||||
|
paragraphStyle.SetLineInset(10.0f);
|
||||||
|
|
||||||
|
paragraph = Paragraph(paragraphStyle);
|
||||||
|
paragraph.Append(TextSpan("Even bullet lists are supported.", regularStyle));
|
||||||
|
document->Append(paragraph);
|
||||||
|
|
||||||
|
paragraph = Paragraph(paragraphStyle);
|
||||||
|
paragraph.Append(TextSpan("The wrapping in this bullet item should "
|
||||||
|
"look visually pleasing. And why should it not?", regularStyle));
|
||||||
|
document->Append(paragraph);
|
||||||
|
|
||||||
documentView->SetTextDocument(document);
|
documentView->SetTextDocument(document);
|
||||||
|
|
||||||
window->Show();
|
window->Show();
|
||||||
|
|||||||
Reference in New Issue
Block a user