diff --git a/games/gorreven.arch b/games/gorreven.arch index b13ba59..e6a18b0 100644 --- a/games/gorreven.arch +++ b/games/gorreven.arch @@ -122,13 +122,13 @@ methods >>my life won't be worth a fake dollar bill. None of our lives will. >> >> ->> Derek Jones Presents... ->> ->> an Archetype Adventure ->> ->> "The Gorreven Papers" ->> ->> + write_centered "Derek Jones Presents..." + write + write_centered "an Archetype Adventure" + write + write_centered "\"The Gorreven Papers\"" + write + write } # FIRSTDESC 'LONGDESC' : diff --git a/games/starship.arch b/games/starship.arch index 221b892..1b4e2b7 100644 --- a/games/starship.arch +++ b/games/starship.arch @@ -96,13 +96,13 @@ methods >> >>Who am I...? >> ->> Derek Jones Presents ->> ->> an Archetype Adventure ->> ->> STARSHIP SOLITAIRE ->> ->> + write_centered "Derek Jones Presents" + write + write_centered "an Archetype Adventure" + write + write_centered "STARSHIP SOLITAIRE" + write + write } # FIRSTDESC 'LONGDESC' : { diff --git a/src/Keywords.cc b/src/Keywords.cc index 0fb0474..759dcae 100644 --- a/src/Keywords.cc +++ b/src/Keywords.cc @@ -68,6 +68,7 @@ namespace archetype { RESERVE(RW_WHILE, "while"); RESERVE(RW_WRITE, "write"); RESERVE(RW_WRITES, "writes"); + RESERVE(RW_WRITE_CENTERED, "write_centered"); OPERATOR(OP_PAIR, "@"); OPERATOR(OP_CONCAT, "&"); diff --git a/src/Keywords.hh b/src/Keywords.hh index e89ac75..bb3f94c 100644 --- a/src/Keywords.hh +++ b/src/Keywords.hh @@ -52,6 +52,7 @@ namespace archetype { RW_WHILE, RW_WRITE, RW_WRITES, + RW_WRITE_CENTERED, RW_WRITE_PARAGRAPH, NumReserved }; diff --git a/src/Statement.cc b/src/Statement.cc index b46cd22..68ddd88 100644 --- a/src/Statement.cc +++ b/src/Statement.cc @@ -477,6 +477,10 @@ namespace archetype { Value OutputStatement::execute() const { Value last_value(new UndefinedValue); + unique_ptr centered; + if (writeType_ == Keywords::RW_WRITE_CENTERED) { + centered.reset(new ostringstream); + } for (auto const& expr : expressions_) { last_value = expr->evaluate(); if (writeType_ == Keywords::RW_DISPLAY) { @@ -488,10 +492,16 @@ namespace archetype { } Value v_s = last_value->stringConversion(); if (v_s->isDefined()) { - Universe::instance().output()->put(v_s->getString()); + if (centered) { + *centered << v_s->getString(); + } else { + Universe::instance().output()->put(v_s->getString()); + } } } - if (writeType_ != Keywords::RW_WRITES) { + if (centered) { + Universe::instance().output()->center(centered->str()); + } else if (writeType_ != Keywords::RW_WRITES) { Universe::instance().output()->endLine(); } if (writeType_ == Keywords::RW_STOP) { @@ -766,6 +776,7 @@ namespace archetype { case Keywords::RW_DISPLAY: case Keywords::RW_WRITE: case Keywords::RW_WRITES: + case Keywords::RW_WRITE_CENTERED: case Keywords::RW_STOP: the_stmt.reset(new OutputStatement(word)); break; diff --git a/src/TestStatement.cc b/src/TestStatement.cc index b049129..6df6310 100644 --- a/src/TestStatement.cc +++ b/src/TestStatement.cc @@ -87,6 +87,14 @@ namespace archetype { string expected_4_2 = "Hello, world!\n"; ARCHETYPE_TEST_EQUAL(actual_4_2, expected_4_2); + // Capture uses a bare StringOutput, so center() falls through to the + // default put+endLine; padding math is covered in TestWrappedOutput. + Statement stmt4c = make_stmt_from_str("write_centered \"Hello, \", \"world!\""); + ARCHETYPE_TEST(stmt4c != nullptr); + Capture capture4c; + stmt4c->execute(); + ARCHETYPE_TEST_EQUAL(capture4c.getCapture(), string("Hello, world!\n")); + Statement stmt5 = make_stmt_from_str(">>Now, this is a \"quote\"."); ARCHETYPE_TEST(stmt5 != nullptr); Capture capture5; diff --git a/src/TestWrappedOutput.cc b/src/TestWrappedOutput.cc index babe35b..bd65e71 100644 --- a/src/TestWrappedOutput.cc +++ b/src/TestWrappedOutput.cc @@ -50,7 +50,44 @@ namespace archetype { out() << "TestWrappedOutput finished." << endl; } + void TestWrappedOutput::testCenter_() { + UserOutput user_soutput{new StringOutput}; + StringOutput& strout(*dynamic_cast(user_soutput.get())); + UserOutput user_output{new WrappedOutput{user_soutput}}; + WrappedOutput& wrout(*dynamic_cast(user_output.get())); + + auto delta = [&](size_t& mark) { + string all = strout.getOutput(); + string d = all.substr(mark); + mark = all.size(); + return d; + }; + size_t mark = 0; + + wrout.setMaxColumns(20); + user_output->center("Hello"); + // (20 - 5) / 2 = 7 leading spaces + ARCHETYPE_TEST_EQUAL(delta(mark), string(" Hello\n")); + + // A line at the column count gets no padding. + wrout.setMaxColumns(5); + user_output->center("Hello"); + ARCHETYPE_TEST_EQUAL(delta(mark), string("Hello\n")); + + // A line wider than the column count gets no padding. + user_output->center("Hello, world!"); + ARCHETYPE_TEST_EQUAL(delta(mark), string("Hello, world!\n")); + + // Zero columns means indeterminate width: emit unpadded. + wrout.setMaxColumns(0); + user_output->center("X"); + ARCHETYPE_TEST_EQUAL(delta(mark), string("X\n")); + + out() << "TestWrappedOutput::testCenter_ finished." << endl; + } + void TestWrappedOutput::runTests_() { testBasicWrap_(); + testCenter_(); } } diff --git a/src/TestWrappedOutput.hh b/src/TestWrappedOutput.hh index a176d48..e257c7b 100644 --- a/src/TestWrappedOutput.hh +++ b/src/TestWrappedOutput.hh @@ -14,6 +14,7 @@ namespace archetype { class TestWrappedOutput : public ITestSuite { void testBasicWrap_(); + void testCenter_(); protected: virtual void runTests_() override; public: diff --git a/src/UserOutput.hh b/src/UserOutput.hh index 806d3a8..00b2f75 100644 --- a/src/UserOutput.hh +++ b/src/UserOutput.hh @@ -24,6 +24,7 @@ namespace archetype { virtual void endLine() = 0; virtual void resetPager() { } virtual void banner(char ch) = 0; + virtual void center(const std::string& line) { put(line); endLine(); } }; typedef std::shared_ptr UserOutput; } diff --git a/src/WrappedOutput.cc b/src/WrappedOutput.cc index 4a79fde..fe58f0d 100644 --- a/src/WrappedOutput.cc +++ b/src/WrappedOutput.cc @@ -80,6 +80,18 @@ namespace archetype { resetCursor(); } + void WrappedOutput::center(const std::string& line) { + if (cursor_ != 0) { + endLine(); + } + if (maxColumns_ > 0 and int(line.size()) < maxColumns_) { + int pad = (maxColumns_ - int(line.size())) / 2; + output_->put(string(pad, ' ')); + } + output_->put(line); + endLine(); + } + void WrappedOutput::banner(char ch) { if (cursor_ != 0) { endLine(); diff --git a/src/WrappedOutput.hh b/src/WrappedOutput.hh index 1e97834..dc59da8 100644 --- a/src/WrappedOutput.hh +++ b/src/WrappedOutput.hh @@ -23,6 +23,7 @@ namespace archetype { virtual void put(const std::string& line) override; virtual void endLine() override; virtual void banner(char ch) override; + virtual void center(const std::string& line) override; // Get and set the columns of text available in the console. // Zero is a special value meaning that the columns are diff --git a/src/main.cc b/src/main.cc index 2632ec1..bebcab7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -58,11 +58,7 @@ namespace archetype { ~Session() { if (not silent_) { - UserOutput output = Universe::instance().output(); - output->endLine(); - output->put("Archetype "); - output->put(VersionString); - output->endLine(); + std::cerr << "Archetype " << VersionString << std::endl; } TestRegistry::destroy(); Universe::destroy();