Merge branch 'html-set-cell'

Make wxHTML API more flexible by allowing to operate on individual
wxHtmlContainerCell instead of only the entire HTML document text.

See https://github.com/wxWidgets/wxWidgets/pull/899
This commit is contained in:
Vadim Zeitlin
2018-08-27 02:17:10 +02:00
7 changed files with 174 additions and 46 deletions

View File

@@ -445,7 +445,7 @@ protected:
class WXDLLIMPEXP_HTML wxHtmlContainerCell : public wxHtmlCell
{
public:
wxHtmlContainerCell(wxHtmlContainerCell *parent);
explicit wxHtmlContainerCell(wxHtmlContainerCell *parent);
virtual ~wxHtmlContainerCell();
virtual void Layout(int w) wxOVERRIDE;
@@ -459,6 +459,11 @@ public:
// insert cell at the end of m_Cells list
void InsertCell(wxHtmlCell *cell);
// Detach a child cell. After calling this method, it's the caller
// responsibility to destroy this cell (possibly by calling InsertCell()
// with it to attach it elsewhere).
void Detach(wxHtmlCell *cell);
// sets horizontal/vertical alignment
void SetAlignHor(int al) {m_AlignHor = al; m_LastLayout = -1;}
int GetAlignHor() const {return m_AlignHor;}

View File

@@ -54,6 +54,11 @@ public:
// (see wxFileSystem for detailed explanation)
void SetHtmlText(const wxString& html, const wxString& basepath = wxEmptyString, bool isdir = true);
// Sets the HTML cell that will be rendered: this is more efficient than
// using text as it allows to parse it only once. Note that the cell will
// be modified by this call.
void SetHtmlCell(wxHtmlContainerCell& cell);
// Sets fonts to be used when displaying HTML page. (if size null then default sizes used).
void SetFonts(const wxString& normal_face, const wxString& fixed_face, const int *sizes = NULL);
@@ -79,11 +84,14 @@ public:
int GetTotalHeight() const;
private:
void DoSetHtmlCell(wxHtmlContainerCell* cell);
wxDC *m_DC;
wxHtmlWinParser *m_Parser;
wxFileSystem *m_FS;
wxFileSystem m_FS;
wxHtmlWinParser m_Parser;
wxHtmlContainerCell *m_Cells;
int m_Width, m_Height;
bool m_ownsCells;
wxDECLARE_NO_COPY_CLASS(wxHtmlDCRenderer);
};

View File

@@ -471,7 +471,21 @@ public:
/**
Constructor. @a parent is pointer to parent container or @NULL.
*/
wxHtmlContainerCell(wxHtmlContainerCell* parent);
explicit wxHtmlContainerCell(wxHtmlContainerCell* parent);
/**
Detach a child cell.
Detaching a cell removes it from this container and allows to reattach
it to another one by using InsertCell(). Alternatively, this method can
be used to selectively remove some elements of the HTML document tree
by deleting the cell after calling it.
@param cell Must be non-null and an immediate child of this cell.
@since 3.1.2
*/
void Detach(wxHtmlCell* cell);
/**
Returns container's horizontal alignment.
@@ -506,6 +520,9 @@ public:
/**
Inserts a new cell into the container.
Note that the container takes ownership of the cell and will delete it
when it itself is destroyed.
*/
void InsertCell(wxHtmlCell* cell);

View File

@@ -168,6 +168,19 @@ public:
const wxString& basepath = wxEmptyString,
bool isdir = true);
/**
Associate the given HTML contents to the renderer.
This is similar to SetHtmlText(), but is more efficient as the text can
be parsed only once, using wxHtmlParser::Parse(), and then passed to
wxHtmlDCRenderer multiple times or already reused for other purposes.
Note that @a cell will be modified (e.g. laid out) by this function.
@since 3.1.2
*/
void SetHtmlCell(wxHtmlContainerCell& cell);
/**
Set size of output rectangle, in pixels. Note that you @b can't change
width of the rectangle between calls to Render() !

View File

@@ -1159,6 +1159,43 @@ void wxHtmlContainerCell::InsertCell(wxHtmlCell *f)
void wxHtmlContainerCell::Detach(wxHtmlCell *cell)
{
wxHtmlCell* const firstChild = GetFirstChild();
if ( cell == firstChild )
{
m_Cells = cell->GetNext();
if ( m_LastCell == cell )
m_LastCell = NULL;
}
else // Not the first child.
{
for ( wxHtmlCell* prev = firstChild;; )
{
wxHtmlCell* const next = prev->GetNext();
// We can't reach the end of the children list without finding this
// cell, normally.
wxCHECK_RET( next, "Detaching cell which is not our child" );
if ( cell == next )
{
prev->SetNext(cell->GetNext());
if ( m_LastCell == cell )
m_LastCell = prev;
break;
}
prev = next;
}
}
cell->SetParent(NULL);
cell->SetNext(NULL);
}
void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag)
{
wxString alg;

View File

@@ -70,9 +70,8 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject()
m_DC = NULL;
m_Width = m_Height = 0;
m_Cells = NULL;
m_Parser = new wxHtmlWinParser();
m_FS = new wxFileSystem();
m_Parser->SetFS(m_FS);
m_ownsCells = false;
m_Parser.SetFS(&m_FS);
SetStandardFonts(DEFAULT_PRINT_FONT_SIZE);
}
@@ -80,9 +79,8 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject()
wxHtmlDCRenderer::~wxHtmlDCRenderer()
{
if (m_Cells) delete m_Cells;
if (m_Parser) delete m_Parser;
if (m_FS) delete m_FS;
if ( m_ownsCells )
delete m_Cells;
}
@@ -90,7 +88,7 @@ wxHtmlDCRenderer::~wxHtmlDCRenderer()
void wxHtmlDCRenderer::SetDC(wxDC *dc, double pixel_scale, double font_scale)
{
m_DC = dc;
m_Parser->SetDC(m_DC, pixel_scale, font_scale);
m_Parser.SetDC(m_DC, pixel_scale, font_scale);
}
@@ -110,19 +108,38 @@ void wxHtmlDCRenderer::SetHtmlText(const wxString& html, const wxString& basepat
wxCHECK_RET( m_DC, "SetDC() must be called before SetHtmlText()" );
wxCHECK_RET( m_Width, "SetSize() must be called before SetHtmlText()" );
wxDELETE(m_Cells);
m_FS.ChangePathTo(basepath, isdir);
m_FS->ChangePathTo(basepath, isdir);
m_Cells = (wxHtmlContainerCell*) m_Parser->Parse(html);
wxHtmlContainerCell* const cell = (wxHtmlContainerCell*) m_Parser.Parse(html);
wxCHECK_RET( cell, "Failed to parse HTML" );
DoSetHtmlCell(cell);
m_ownsCells = true;
}
void wxHtmlDCRenderer::DoSetHtmlCell(wxHtmlContainerCell* cell)
{
if ( m_ownsCells )
delete m_Cells;
m_Cells = cell;
m_Cells->SetIndent(0, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
m_Cells->Layout(m_Width);
}
void wxHtmlDCRenderer::SetHtmlCell(wxHtmlContainerCell& cell)
{
DoSetHtmlCell(&cell);
m_ownsCells = false;
}
void wxHtmlDCRenderer::SetFonts(const wxString& normal_face, const wxString& fixed_face,
const int *sizes)
{
m_Parser->SetFonts(normal_face, fixed_face, sizes);
m_Parser.SetFonts(normal_face, fixed_face, sizes);
if ( m_Cells )
m_Cells->Layout(m_Width);
@@ -133,7 +150,7 @@ void wxHtmlDCRenderer::SetStandardFonts(int size,
const wxString& normal_face,
const wxString& fixed_face)
{
m_Parser->SetStandardFonts(size, normal_face, fixed_face);
m_Parser.SetStandardFonts(size, normal_face, fixed_face);
if ( m_Cells )
m_Cells->Layout(m_Width);

View File

@@ -24,37 +24,8 @@
#include "wx/html/winpars.h"
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class HtmlParserTestCase : public CppUnit::TestCase
{
public:
HtmlParserTestCase() { }
private:
CPPUNIT_TEST_SUITE( HtmlParserTestCase );
CPPUNIT_TEST( Invalid );
CPPUNIT_TEST_SUITE_END();
void Invalid();
wxDECLARE_NO_COPY_CLASS(HtmlParserTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( HtmlParserTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HtmlParserTestCase, "HtmlParserTestCase" );
// ----------------------------------------------------------------------------
// tests themselves
// ----------------------------------------------------------------------------
// Test that parsing invalid HTML simply fails but doesn't crash for example.
void HtmlParserTestCase::Invalid()
TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]")
{
class NullParser : public wxHtmlWinParser
{
@@ -75,4 +46,64 @@ void HtmlParserTestCase::Invalid()
p.Parse("<!---");
}
TEST_CASE("wxHtmlCell::Detach", "[html][cell]")
{
wxMemoryDC dc;
wxHtmlContainerCell* const top = new wxHtmlContainerCell(NULL);
wxHtmlContainerCell* const cont = new wxHtmlContainerCell(NULL);
wxHtmlCell* const cell1 = new wxHtmlWordCell("Hello", dc);
wxHtmlCell* const cell2 = new wxHtmlColourCell(*wxRED);
wxHtmlCell* const cell3 = new wxHtmlWordCell("world", dc);
cont->InsertCell(cell1);
cont->InsertCell(cell2);
cont->InsertCell(cell3);
top->InsertCell(cont);
SECTION("container")
{
top->Detach(cont);
CHECK( top->GetFirstChild() == NULL );
delete cont;
}
SECTION("first-child")
{
cont->Detach(cell1);
CHECK( cont->GetFirstChild() == cell2 );
delete cell1;
}
SECTION("middle-child")
{
cont->Detach(cell2);
CHECK( cont->GetFirstChild() == cell1 );
CHECK( cell1->GetNext() == cell3 );
delete cell2;
}
SECTION("last-child")
{
cont->Detach(cell3);
CHECK( cont->GetFirstChild() == cell1 );
CHECK( cell1->GetNext() == cell2 );
CHECK( cell2->GetNext() == NULL );
delete cell3;
}
SECTION("invalid")
{
WX_ASSERT_FAILS_WITH_ASSERT_MESSAGE
(
"Expected assertion for detaching non-child",
top->Detach(cell1);
);
}
}
#endif //wxUSE_HTML