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 =
|
||||
Bullet.cpp
|
||||
BulletData.cpp
|
||||
CharacterStyle.cpp
|
||||
CharacterStyleData.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);
|
||||
_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();
|
||||
|
||||
float x = fParagraphStyle.FirstLineInset();
|
||||
const Bullet& bullet = fParagraphStyle.Bullet();
|
||||
|
||||
float x = fParagraphStyle.LineInset() + fParagraphStyle.FirstLineInset()
|
||||
+ bullet.Spacing();
|
||||
float y = 0.0f;
|
||||
int lineIndex = 0;
|
||||
int lineStart = 0;
|
||||
@@ -388,7 +402,7 @@ ParagraphLayout::_Layout()
|
||||
_FinalizeLine(lineStart, lineEnd, lineIndex, y, lineHeight);
|
||||
|
||||
// Start position of the next line
|
||||
x = fParagraphStyle.LineInset();
|
||||
x = fParagraphStyle.LineInset() + bullet.Spacing();
|
||||
y += lineHeight + fParagraphStyle.LineSpacing();
|
||||
|
||||
if (lineBreak)
|
||||
@@ -642,7 +656,7 @@ ParagraphLayout::_FinalizeLine(int lineStart, int lineEnd, int lineIndex,
|
||||
if (addSpan) {
|
||||
const TextSpan& span = fTextSpans.ItemAt(spanIndex);
|
||||
TextSpan subSpan = span.SubSpan(i - spanStart,
|
||||
(lineEnd - spanStart) - (i - spanStart));
|
||||
(lineEnd - spanStart + 1) - (i - spanStart));
|
||||
line.layoutedSpans.Add(subSpan);
|
||||
_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);
|
||||
float SpacingBottom() const;
|
||||
|
||||
bool SetBullet(const ::Bullet& bullet);
|
||||
const ::Bullet& Bullet() const;
|
||||
|
||||
private:
|
||||
ParagraphStyleDataRef fStyleData;
|
||||
|
||||
@@ -17,7 +17,9 @@ ParagraphStyleData::ParagraphStyleData()
|
||||
fLineInset(0.0f),
|
||||
|
||||
fSpacingTop(0.0f),
|
||||
fSpacingBottom(0.0f)
|
||||
fSpacingBottom(0.0f),
|
||||
|
||||
fBullet()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -31,7 +33,9 @@ ParagraphStyleData::ParagraphStyleData(const ParagraphStyleData& other)
|
||||
fLineInset(other.fLineInset),
|
||||
|
||||
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
|
||||
&& fLineInset == other.fLineInset
|
||||
&& 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
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#ifndef PARAGRAPH_STYLE_DATA_H
|
||||
#define PARAGRAPH_STYLE_DATA_H
|
||||
|
||||
#include <Referenceable.h>
|
||||
#include "Bullet.h"
|
||||
|
||||
|
||||
enum Alignment {
|
||||
@@ -60,6 +60,9 @@ public:
|
||||
inline float SpacingBottom() const
|
||||
{ return fSpacingBottom; }
|
||||
|
||||
ParagraphStyleDataRef SetBullet(const ::Bullet& bullet);
|
||||
inline const ::Bullet& Bullet() const
|
||||
{ return fBullet; }
|
||||
private:
|
||||
ParagraphStyleData& operator=(const ParagraphStyleData& other);
|
||||
|
||||
@@ -73,6 +76,8 @@ private:
|
||||
|
||||
float fSpacingTop;
|
||||
float fSpacingBottom;
|
||||
|
||||
::Bullet fBullet;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ TextDocumentTest::ReadyToRun()
|
||||
TextDocumentRef document(new TextDocument(), true);
|
||||
|
||||
Paragraph paragraph(paragraphStyle);
|
||||
paragraph.Append(TextSpan("This is a ", regularStyle));
|
||||
paragraph.Append(TextSpan("test", bigStyle));
|
||||
paragraph.Append(TextSpan(" to see if ", regularStyle));
|
||||
paragraph.Append(TextSpan("This is a", regularStyle));
|
||||
paragraph.Append(TextSpan(" test ", bigStyle));
|
||||
paragraph.Append(TextSpan("to see if ", regularStyle));
|
||||
paragraph.Append(TextSpan("different", boldStyle));
|
||||
paragraph.Append(TextSpan(" character styles already work.", regularStyle));
|
||||
document->Append(paragraph);
|
||||
@@ -70,6 +70,22 @@ TextDocumentTest::ReadyToRun()
|
||||
paragraph.Append(TextSpan(" supported as of now!", regularStyle));
|
||||
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);
|
||||
|
||||
window->Show();
|
||||
|
||||
Reference in New Issue
Block a user