diff --git a/CMakeLists.txt b/CMakeLists.txt index a375974e7..e9d0fa089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,14 +65,11 @@ setup_third_party( GIT_REPOSITORY https://github.com/Awesome-Embedded-Learning-Studio/QuarkWidgets.git GIT_TAG v0.1.0) -# Standalone desktop apps. Each ships a standalone executable launched via -# QProcess. (Historically apps/ had to precede desktop because desktop reused -# the calculator parser lib defined here; that coupling has been removed -- the -# desktop core no longer reaches into apps/. apps/ now only needs to precede -# test/, which it does; the apps-before-desktop ordering below is convention.) -log_module_start("apps") -add_subdirectory(apps) -log_module_end("apps") +# Standalone desktop apps now live in the separate `CFDeskit` repo +# (https://github.com/Awesome-Embedded-Learning-Studio/CFDeskit). The main +# repo no longer compiles apps — they are deployed as prebuilt artifacts into +# /../apps// at install/runtime, where AppDiscoverer scans them. +# See apps/README.md for the deployment contract. log_module_start("desktop") add_subdirectory(desktop) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt deleted file mode 100644 index 7354d686f..000000000 --- a/apps/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Standalone desktop apps. -# Each entry is an independent executable that CFDesktop launches via -# AppLaunchService (QProcess) — they are not linked into the desktop shell, -# so a crash or hang in an app cannot take down the desktop. -add_subdirectory(calculator) -add_subdirectory(noter) -add_subdirectory(system_state) -add_subdirectory(alarm_clock) -add_subdirectory(calendar) diff --git a/apps/README.md b/apps/README.md new file mode 100644 index 000000000..af02052bd --- /dev/null +++ b/apps/README.md @@ -0,0 +1,35 @@ +# apps/ — deployment target (no source) + +This directory is the **runtime deployment target** for standalone desktop apps. +App sources no longer live in this repo — they were extracted to the separate +[`CFDeskit`](https://github.com/Awesome-Embedded-Learning-Studio/CFDeskit) +repository (calculator, noter, alarm_clock, calendar, system_state). + +## Deployment contract + +`AppDiscoverer` (in `desktop/ui/components/launcher/`) scans +`/../apps//app.json` at startup. Each app ships as a self-contained +package: + +``` +/apps/ + libquarkwidgets.so # shared by all apps (rpath $ORIGIN/..) + / + # executable + app.json # manifest (app_id, display_name, exec, launch_kind) +``` + +## How to deploy + +Build CFDeskit and install (or copy) its `apps/` tree here: + +```bash +# clean install: +( cd ~/CFDeskit && cmake --install build --prefix ) +# quick local test against an existing CFDesktop build tree: +cp -r ~/CFDeskit/build/apps/. /out/build_develop/apps/ +``` + +Apps resolve `libquarkwidgets.so` via rpath `$ORIGIN/..` and runtime-verify the +ABI version (`abi_check.hpp`), so the apps' QuarkWidgets version is fully +decoupled from the desktop's `bin/libquarkwidgets.so`. diff --git a/apps/alarm_clock/CMakeLists.txt b/apps/alarm_clock/CMakeLists.txt deleted file mode 100644 index 49f5ff75a..000000000 --- a/apps/alarm_clock/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# Alarm Clock standalone application. -# Ported from CCIMXDesktop's AlarmyClock; UI uses QuarkWidgets MD3 widgets. -# CFDesktop launches this binary via AppLaunchService (QProcess), so it runs in -# its own process (isolated from the desktop shell). - -# Standalone Alarm Clock executable (consumed by CFDesktop via QProcess launch). -qt_add_executable(alarm_clock - main.cpp - alarm_clock_panel.cpp -) - -target_link_libraries(alarm_clock PRIVATE - QuarkWidgets::quarkwidgets - Qt6::Widgets -) - -# App package is self-contained under /../apps/alarm_clock/ (executable + -# manifest in the same dir). AppDiscoverer scans /../apps//app.json, -# resolves exec to the co-located absolute path, and AppLaunchService launches -# it directly — no working-directory dependency, no PATH search needed. -set(ALARM_CLOCK_APP_DIR "${CMAKE_BINARY_DIR}/apps/alarm_clock") -set_target_properties(alarm_clock PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${ALARM_CLOCK_APP_DIR}" -) - -# Deploy the manifest next to the executable (at configure time). -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app.json" "${ALARM_CLOCK_APP_DIR}/app.json" COPYONLY) diff --git a/apps/alarm_clock/alarm_clock_panel.cpp b/apps/alarm_clock/alarm_clock_panel.cpp deleted file mode 100644 index 5263e59ba..000000000 --- a/apps/alarm_clock/alarm_clock_panel.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/** - * @file apps/alarm_clock/alarm_clock_panel.cpp - * @brief Implementation of the Alarm Clock panel. - * - * Ported from CCIMXDesktop's AlarmyClock. The original AlarmyNotifier polling - * loop (1 s QTimer + per-second QTime match) is preserved verbatim in @ref - * onTick; the broadcaster/processor event bus is collapsed into the direct - * @ref fireAlarm call, and the QPainter analog dial + QMainWindow/.ui shell - * are replaced by a QuarkWidgets MD3 panel built in code. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup alarm_clock - */ - -#include "alarm_clock_panel.h" - -#include "ui/widget/material/widget/button/button.h" -#include "ui/widget/material/widget/label/label.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cf::desktop::desktop_component { - -namespace { -using qw::widget::material::Button; -using qw::widget::material::Label; -using qw::widget::material::TypographyStyle; -using Variant = Button::ButtonVariant; - -constexpr qreal kCornerRadius = 16.0; ///< Card corner radius (px). - -/// @brief Qt item-data role key for the stored AlarmEntry. -constexpr int kAlarmRole = Qt::UserRole + 1; - -/// @brief Renders an alarm entry as "hh:mm — note". -QString formatEntry(const AlarmEntry& e) { - return e.time.toString(QStringLiteral("hh:mm")) + QStringLiteral(" — ") + - (e.note.isEmpty() ? QStringLiteral("(no note)") : e.note); -} -} // namespace - -AlarmClockPanel::AlarmClockPanel(QWidget* parent) : QWidget(parent) { - setupUi(); - - // Poll the wall clock once per second (mirrors AlarmyNotifier::check_time). - ticker_ = new QTimer(this); - ticker_->setInterval(1000); - connect(ticker_, &QTimer::timeout, this, &AlarmClockPanel::onTick); - ticker_->start(); - onTick(); -} - -AlarmClockPanel::~AlarmClockPanel() = default; - -void AlarmClockPanel::setupUi() { - auto* layout = new QVBoxLayout(this); - layout->setContentsMargins(16, 16, 16, 16); - layout->setSpacing(12); - - // Live clock readout. - clock_label_ = new Label("--:--:--", TypographyStyle::DisplayLarge, this); - clock_label_->setAlignment(Qt::AlignCenter); - layout->addWidget(clock_label_); - - // --- Time editor row: [- hour +] : [- minute +] --- - auto* editor_row = new QHBoxLayout; - editor_row->setSpacing(6); - - auto make_spin = [this](int max) { - auto* spin = new QSpinBox(this); - spin->setRange(0, max); - spin->setFixedHeight(40); - spin->setButtonSymbols(QSpinBox::PlusMinus); - return spin; - }; - hour_spin_ = make_spin(23); - hour_spin_->setValue(QTime::currentTime().hour()); - minute_spin_ = make_spin(59); - minute_spin_->setValue(QTime::currentTime().minute()); - - auto make_step = [this](const QString& glyph, Variant v) { - auto* btn = new Button(glyph, v, this); - btn->setFixedSize(40, 40); - return btn; - }; - auto* h_minus = make_step(QStringLiteral("-"), Variant::Tonal); - auto* h_plus = make_step(QStringLiteral("+"), Variant::Tonal); - auto* m_minus = make_step(QStringLiteral("-"), Variant::Tonal); - auto* m_plus = make_step(QStringLiteral("+"), Variant::Tonal); - - connect(h_minus, &QPushButton::clicked, this, - [this] { hour_spin_->setValue(hour_spin_->value() - 1); }); - connect(h_plus, &QPushButton::clicked, this, - [this] { hour_spin_->setValue(hour_spin_->value() + 1); }); - connect(m_minus, &QPushButton::clicked, this, - [this] { minute_spin_->setValue(minute_spin_->value() - 1); }); - connect(m_plus, &QPushButton::clicked, this, - [this] { minute_spin_->setValue(minute_spin_->value() + 1); }); - - editor_row->addWidget(h_minus); - editor_row->addWidget(hour_spin_); - editor_row->addWidget(h_plus); - auto* colon = new QLabel(QStringLiteral(":"), this); - colon->setAlignment(Qt::AlignCenter); - QFont colon_font = colon->font(); - colon_font.setPointSize(20); - colon->setFont(colon_font); - editor_row->addWidget(colon); - editor_row->addWidget(m_minus); - editor_row->addWidget(minute_spin_); - editor_row->addWidget(m_plus); - editor_row->addStretch(); - layout->addLayout(editor_row); - - // Note editor. - note_edit_ = new QTextEdit(this); - note_edit_->setPlaceholderText(QStringLiteral("Reminder note (optional)")); - note_edit_->setFixedHeight(60); - layout->addWidget(note_edit_); - - // Add / Remove control row. - auto* control_row = new QHBoxLayout; - control_row->setSpacing(8); - auto* add_btn = new Button(QStringLiteral("Add Alarm"), Variant::Filled, this); - auto* remove_btn = new Button(QStringLiteral("Remove Selected"), Variant::Outlined, this); - control_row->addWidget(add_btn); - control_row->addWidget(remove_btn); - control_row->addStretch(); - layout->addLayout(control_row); - - // Armed alarms list. - alarm_list_ = new QListWidget(this); - layout->addWidget(alarm_list_, /*stretch=*/1); - - connect(add_btn, &QPushButton::clicked, this, &AlarmClockPanel::onAddAlarm); - connect(remove_btn, &QPushButton::clicked, this, &AlarmClockPanel::onRemoveAlarm); -} - -void AlarmClockPanel::onTick() { - const QTime now = QTime::currentTime(); - clock_label_->setText(now.toString(QStringLiteral("hh:mm:ss"))); - - // Match hour+minute+second against every armed alarm (the original - // AlarmyNotifier matches all three fields; entries are stored with second - // forced to 0, so an alarm fires in the first second of its minute). - for (int i = 0; i < alarm_list_->count(); ++i) { - auto* item = alarm_list_->item(i); - const auto entry = item->data(kAlarmRole).value(); - if (entry.time.hour() == now.hour() && entry.time.minute() == now.minute() && - entry.time.second() == now.second()) { - fireAlarm(item); - } - } -} - -void AlarmClockPanel::onAddAlarm() { - AlarmEntry entry; - entry.time = QTime(hour_spin_->value(), minute_spin_->value(), 0); - entry.note = note_edit_->toPlainText().trimmed(); - - // Reject duplicates at the same minute to avoid a double-fire. - for (int i = 0; i < alarm_list_->count(); ++i) { - const auto existing = alarm_list_->item(i)->data(kAlarmRole).value(); - if (existing.time.hour() == entry.time.hour() && - existing.time.minute() == entry.time.minute()) { - return; - } - } - - auto* item = new QListWidgetItem(formatEntry(entry), alarm_list_); - item->setData(kAlarmRole, QVariant::fromValue(entry)); - alarm_list_->addItem(item); - - note_edit_->clear(); -} - -void AlarmClockPanel::onRemoveAlarm() { - auto selected = alarm_list_->selectedItems(); - for (auto* item : selected) { - delete item; - } -} - -void AlarmClockPanel::fireAlarm(QListWidgetItem* item) { - const auto entry = item->data(kAlarmRole).value(); - - // TODO(alarm_clock): add audible ringing. The CCIMX original had no audio - // asset either (it popped a QMessageBox). Wire QSoundEffect with a bundled - // .wav (requires Qt6::Multimedia + a qrc asset) or a platform system sound - // here. For now the visual alert mirrors the original DefaultProcessor. - QMessageBox::information( - this, QStringLiteral("Alarm"), - QStringLiteral("⏰ %1\n\n%2") - .arg(entry.time.toString(QStringLiteral("hh:mm")), - entry.note.isEmpty() ? QStringLiteral("Time's up!") : entry.note)); - - // One-shot: disarm after firing. - delete item; -} - -void AlarmClockPanel::paintEvent(QPaintEvent* /*event*/) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing, true); - const QColor surface_color(0xF7, 0xF5, 0xF3); - QPainterPath card; - card.addRoundedRect(QRectF(rect()), kCornerRadius, kCornerRadius); - p.fillPath(card, surface_color); -} - -} // namespace cf::desktop::desktop_component diff --git a/apps/alarm_clock/alarm_clock_panel.h b/apps/alarm_clock/alarm_clock_panel.h deleted file mode 100644 index c20957ce5..000000000 --- a/apps/alarm_clock/alarm_clock_panel.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @file apps/alarm_clock/alarm_clock_panel.h - * @brief Alarm Clock main panel (standalone app). - * - * Root widget of the standalone Alarm Clock executable. Provides a live - * digital clock readout, a time editor (hour/minute spinboxes with +/- MD3 - * buttons), a note field, an add/remove control row, and a list of armed - * alarms. Each armed alarm is checked against the wall clock once per second; - * when its time arrives the alarm fires and a Material message box shows the - * note. Ported from CCIMXDesktop's AlarmyClock — the original QPainter analog - * dial, QMainWindow shell, .ui files, and broadcaster/processor event bus are - * replaced by a single QuarkWidgets MD3 panel built in code. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup alarm_clock - */ - -#pragma once - -#include -#include -#include - -class QListWidget; -class QListWidgetItem; -class QPaintEvent; -class QSpinBox; -class QTextEdit; -class QTimer; - -namespace qw::widget::material { -class Button; -class Label; -} // namespace qw::widget::material - -namespace cf::desktop::desktop_component { - -/** - * @brief Represents one armed alarm (target time + user note). - * - * @ingroup alarm_clock - */ -struct AlarmEntry { - QTime time; ///< Target wall-clock time (hh:mm, seconds ignored). - QString note; ///< Reminder text shown when the alarm fires. -}; - -/** - * @brief Root widget of the standalone Alarm Clock application. - * - * @ingroup alarm_clock - */ -class AlarmClockPanel final : public QWidget { - Q_OBJECT - public: - /** - * @brief Constructs the Alarm Clock panel. - * - * @param[in] parent Parent widget (nullptr for a top-level window). - * - * @throws None - * @since 0.20 - * @ingroup alarm_clock - */ - explicit AlarmClockPanel(QWidget* parent = nullptr); - - /** - * @brief Destructs the panel. - * - * @throws None - * @since 0.20 - * @ingroup alarm_clock - */ - ~AlarmClockPanel() override; - - protected: - /** - * @brief Paints the rounded Material card background. - * - * @param[in] event The paint event descriptor. - * - * @throws None - * @since 0.20 - * @ingroup alarm_clock - */ - void paintEvent(QPaintEvent* event) override; - - private slots: - /// @brief Refreshes the live clock label from the wall clock. - void onTick(); - /// @brief Arms a new alarm from the editor fields. - void onAddAlarm(); - /// @brief Removes the currently selected armed alarm. - void onRemoveAlarm(); - - private: - /** - * @brief Builds the editor + armed-alarms layout. - * - * @throws None - * @since 0.20 - * @ingroup alarm_clock - */ - void setupUi(); - - /** - * @brief Fires the alarm identified by @p item. - * - * Shows a Material message box with the alarm note and, if the alarm is - * non-repeating, disarms it. Audible ringing is a TODO (see @ref onTick). - * - * @param[in] item The list item whose alarm time has arrived. - * - * @throws None - * @since 0.20 - * @ingroup alarm_clock - */ - void fireAlarm(QListWidgetItem* item); - - /// @brief Live digital clock readout. - qw::widget::material::Label* clock_label_{nullptr}; - QSpinBox* hour_spin_{nullptr}; ///< Hour editor (0-23). - QSpinBox* minute_spin_{nullptr}; ///< Minute editor (0-59). - QTextEdit* note_edit_{nullptr}; ///< Reminder note editor. - QListWidget* alarm_list_{nullptr}; ///< Armed alarms list. - QTimer* ticker_{nullptr}; ///< Per-second wall-clock poll. -}; - -} // namespace cf::desktop::desktop_component - -/// @brief Enables storing AlarmEntry in a QVariant (QListWidgetItem data). -Q_DECLARE_METATYPE(cf::desktop::desktop_component::AlarmEntry) diff --git a/apps/alarm_clock/app.json b/apps/alarm_clock/app.json deleted file mode 100644 index 53722e853..000000000 --- a/apps/alarm_clock/app.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "app_id": "alarm_clock", - "display_name": "Alarm Clock", - "exec": "alarm_clock", - "launch_kind": "auto" -} diff --git a/apps/alarm_clock/main.cpp b/apps/alarm_clock/main.cpp deleted file mode 100644 index 613e76d1e..000000000 --- a/apps/alarm_clock/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file apps/alarm_clock/main.cpp - * @brief Standalone Alarm Clock application entry point. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup alarm_clock - */ - -#include "alarm_clock_panel.h" - -#include -#include - -int main(int argc, char* argv[]) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("CFDesktop Alarm Clock")); - - cf::desktop::desktop_component::AlarmClockPanel panel; - panel.setWindowTitle(QStringLiteral("Alarm Clock")); - panel.resize(380, 560); - panel.show(); - - return app.exec(); -} diff --git a/apps/calculator/CMakeLists.txt b/apps/calculator/CMakeLists.txt deleted file mode 100644 index fd45f36e9..000000000 --- a/apps/calculator/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# Calculator standalone application. -# Ported from CCIMXDesktop's Caculator; UI uses cfui MD3 widgets. -# CFDesktop launches this binary via AppLaunchService (QProcess), so it runs in -# its own process (isolated from the desktop shell). - -# Expression parser library (QString-based; lives outside base/ because base/ -# must not depend on Qt). -add_library(cfdesktop_calculator_parser STATIC - core/Parser.cpp - core/NumberNode.cpp - core/BinaryOpTreeNode.cpp - core/UnaryOpTreeNode.cpp - core/FunctorTreeNode.cpp - core/ExpressionEvaluator.cpp -) - -target_include_directories(cfdesktop_calculator_parser PUBLIC - $ - $ -) - -target_link_libraries(cfdesktop_calculator_parser PUBLIC Qt6::Core) - -# Standalone Calculator executable (consumed by CFDesktop via QProcess launch). -qt_add_executable(calculator - main.cpp - calculator_panel.cpp -) - -target_link_libraries(calculator PRIVATE - cfdesktop_calculator_parser - QuarkWidgets::quarkwidgets - Qt6::Widgets -) - -# App package is self-contained under /../apps/calculator/ (executable + -# manifest in the same dir). AppDiscoverer scans /../apps//app.json, -# resolves exec to the co-located absolute path, and AppLaunchService launches -# it directly — no working-directory dependency, no PATH search needed. -set(CALC_APP_DIR "${CMAKE_BINARY_DIR}/apps/calculator") -set_target_properties(calculator PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CALC_APP_DIR}" -) - -# Deploy the manifest next to the executable (at configure time). -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app.json" "${CALC_APP_DIR}/app.json" COPYONLY) diff --git a/apps/calculator/app.json b/apps/calculator/app.json deleted file mode 100644 index 45a82c67b..000000000 --- a/apps/calculator/app.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "app_id": "calculator", - "display_name": "Calculator", - "exec": "calculator", - "launch_kind": "detached" -} diff --git a/apps/calculator/calculator_panel.cpp b/apps/calculator/calculator_panel.cpp deleted file mode 100644 index 5f65895cd..000000000 --- a/apps/calculator/calculator_panel.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * @file apps/calculator/calculator_panel.cpp - * @brief Implementation of the Calculator panel. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "calculator_panel.h" - -#include "core/ExpressionEvaluator.h" - -#include "ui/widget/material/widget/button/button.h" -#include "ui/widget/material/widget/label/label.h" - -#include -#include -#include -#include -#include - -#include - -namespace cf::desktop::desktop_component { - -namespace { -using calculator_core::ExpressionEvaluator::evalute_expression; -using qw::widget::material::Button; -using qw::widget::material::Label; -using qw::widget::material::TypographyStyle; -using Variant = Button::ButtonVariant; - -constexpr qreal kCornerRadius = 16.0; ///< Card corner radius (px). - -/// @brief Button definition: text + MD3 variant, laid out row-major in 4 cols. -struct BtnDef { - QString text; - Variant variant; -}; - -/// Keypad layout: digits + operators + scientific functions + CLR/DEL. -const QVector kButtons = { - {"7", Variant::Filled}, {"8", Variant::Filled}, {"9", Variant::Filled}, - {"/", Variant::Tonal}, {"4", Variant::Filled}, {"5", Variant::Filled}, - {"6", Variant::Filled}, {"*", Variant::Tonal}, {"1", Variant::Filled}, - {"2", Variant::Filled}, {"3", Variant::Filled}, {"-", Variant::Tonal}, - {"0", Variant::Filled}, {".", Variant::Filled}, {"=", Variant::Filled}, - {"+", Variant::Tonal}, {"sin", Variant::Outlined}, {"cos", Variant::Outlined}, - {"tan", Variant::Outlined}, {"sqrt", Variant::Outlined}, {"log", Variant::Outlined}, - {"exp", Variant::Outlined}, {"(", Variant::Tonal}, {")", Variant::Tonal}, - {"^", Variant::Tonal}, {"CLR", Variant::Text}, {"DEL", Variant::Text}, - {"", Variant::Text}, -}; - -/// @brief Returns true if @p token is a named function (auto-appends '('). -bool isFunction(const QString& token) { - return token == "sin" || token == "cos" || token == "tan" || token == "sqrt" || - token == "log" || token == "exp"; -} -} // namespace - -CalculatorPanel::CalculatorPanel(QWidget* parent) : QWidget(parent) { - auto* layout = new QVBoxLayout(this); - layout->setContentsMargins(16, 16, 16, 16); - layout->setSpacing(12); - - display_ = new Label("0", TypographyStyle::DisplayMedium, this); - display_->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - layout->addWidget(display_); - - auto* grid = new QGridLayout; - grid->setSpacing(8); - for (int i = 0; i < kButtons.size(); ++i) { - const auto& def = kButtons[i]; - if (def.text.isEmpty()) { - continue; - } - auto* btn = new Button(def.text, def.variant, this); - const QString token = def.text; - connect(btn, &QPushButton::clicked, this, [this, token]() { onButton(token); }); - grid->addWidget(btn, i / 4, i % 4); - } - layout->addLayout(grid); -} - -CalculatorPanel::~CalculatorPanel() = default; - -void CalculatorPanel::onButton(const QString& text) { - if (text == "=") { - try { - const double result = evalute_expression(expression_); - expression_ = QString::number(result); - } catch (const std::exception& e) { - // No silent fallback: show the parser's error message. - expression_ = QString::fromUtf8(e.what()); - } - } else if (text == "CLR") { - expression_.clear(); - } else if (text == "DEL") { - expression_.chop(1); - } else if (isFunction(text)) { - expression_ += text + "("; - } else { - expression_ += text; - } - refreshDisplay(); -} - -void CalculatorPanel::refreshDisplay() { - display_->setText(expression_.isEmpty() ? QStringLiteral("0") : expression_); -} - -void CalculatorPanel::paintEvent(QPaintEvent* /*event*/) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing, true); - const QColor surface_color(0xF7, 0xF5, 0xF3); - QPainterPath card; - card.addRoundedRect(QRectF(rect()), kCornerRadius, kCornerRadius); - p.fillPath(card, surface_color); -} - -void CalculatorPanel::keyPressEvent(QKeyEvent* event) { - const int key = event->key(); - if (key == Qt::Key_Backspace) { - onButton("DEL"); - return; - } - if (key == Qt::Key_Return || key == Qt::Key_Enter) { - onButton("="); - return; - } - if (key == Qt::Key_Escape) { - close(); - return; - } - const QString ch = event->text(); - if (ch.length() == 1) { - const QChar c = ch[0]; - if (c.isDigit() || QString("+-*/^().").contains(c)) { - expression_ += ch; - refreshDisplay(); - return; - } - } - QWidget::keyPressEvent(event); -} - -} // namespace cf::desktop::desktop_component diff --git a/apps/calculator/calculator_panel.h b/apps/calculator/calculator_panel.h deleted file mode 100644 index a60f0ccea..000000000 --- a/apps/calculator/calculator_panel.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file apps/calculator/calculator_panel.h - * @brief Calculator main panel (standalone app). - * - * The root widget of the standalone Calculator executable. Renders the - * keypad with cfui MD3 widgets (Button grid + Label display) and reuses the - * ported calculator_core expression parser. CFDesktop launches this binary - * via AppLaunchService (QProcess), so it runs in its own process. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include -#include - -class QKeyEvent; -class QPaintEvent; - -namespace qw::widget::material { -class Label; -} - -namespace cf::desktop::desktop_component { - -/** - * @brief Root widget of the standalone Calculator application. - * - * @ingroup calculator - */ -class CalculatorPanel final : public QWidget { - Q_OBJECT - public: - /** - * @brief Constructs the Calculator panel. - * @param[in] parent Parent widget (nullptr for a top-level window). - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - explicit CalculatorPanel(QWidget* parent = nullptr); - - /** - * @brief Destructs the panel. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - ~CalculatorPanel() override; - - protected: - /** - * @brief Paints the rounded Material card background. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - void paintEvent(QPaintEvent* event) override; - - /** - * @brief Dispatches keyboard input to the calculator. - * @param[in] event The key event. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - void keyPressEvent(QKeyEvent* event) override; - - private: - /** - * @brief Handles a button token (append, evaluate, clear, etc.). - * @param[in] text The button text. - * @throws None internally; parser exceptions are caught and shown. - * @since 0.20 - * @ingroup calculator - */ - void onButton(const QString& text); - - /// @brief Syncs the display label with the current expression. - void refreshDisplay(); - - qw::widget::material::Label* display_{nullptr}; ///< Read-out display. - QString expression_; ///< Current expression string. -}; - -} // namespace cf::desktop::desktop_component diff --git a/apps/calculator/core/BinaryOpTreeNode.cpp b/apps/calculator/core/BinaryOpTreeNode.cpp deleted file mode 100644 index 8cc4544e6..000000000 --- a/apps/calculator/core/BinaryOpTreeNode.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/BinaryOpTreeNode.cpp - * @brief Implementation of BinaryOpTreeNode. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "BinaryOpTreeNode.h" - -#include -#include -#include - -namespace cf::desktop::desktop_component::calculator_core { - -namespace { -/// Mapping of operator character to the binary arithmetic lambda. -const QMap> kMappings = { - {'+', [](double a, double b) { return a + b; }}, - {'-', [](double a, double b) { return a - b; }}, - {'*', [](double a, double b) { return a * b; }}, - {'/', [](double a, double b) { return b != 0 ? a / b : throw DivideZeroException(); }}, - {'^', [](double a, double b) { return std::pow(a, b); }}, -}; -} // namespace - -BinaryOpTreeNode::BinaryOpTreeNode(const QChar op, TreeNodeBase* left_hand, - TreeNodeBase* right_hand) - : op(op), left_hand(left_hand), right_hand(right_hand) {} - -BinaryOpTreeNode::~BinaryOpTreeNode() { - delete left_hand; - delete right_hand; -} - -double BinaryOpTreeNode::evaluate() const { - const double l = left_hand->evaluate(); - const double r = right_hand->evaluate(); - const auto it = kMappings.find(op); - if (it != kMappings.end()) { - return it.value()(l, r); - } - throw UnSupportedSymbol(QString(op)); -} - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/BinaryOpTreeNode.h b/apps/calculator/core/BinaryOpTreeNode.h deleted file mode 100644 index 5486dd008..000000000 --- a/apps/calculator/core/BinaryOpTreeNode.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/BinaryOpTreeNode.h - * @brief AST node for a binary operation (+, -, *, /, ^). - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include "ParseExceptions.h" -#include "TreeNodeBase.h" -#include - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief AST node applying a binary operator to two subtrees. - * - * Owns its operand nodes and deletes them on destruction. - * - * @ingroup calculator - */ -struct BinaryOpTreeNode : TreeNodeBase { - /** - * @brief Constructs the node. - * @param[in] op The operator character (+, -, *, /, ^). - * @param[in] left_hand The left operand subtree. Ownership transfers. - * @param[in] right_hand The right operand subtree. Ownership transfers. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - BinaryOpTreeNode(QChar op, TreeNodeBase* left_hand, TreeNodeBase* right_hand); - - BinaryOpTreeNode() = delete; - ~BinaryOpTreeNode() override; - - /** - * @brief Evaluates both operands and applies the operator. - * @return The computed value. - * @throws DivideZeroException on division by zero; UnSupportedSymbol on - * an unknown operator. - * @since 0.20 - * @ingroup calculator - */ - double evaluate() const override; - - private: - QChar op; ///< The operator character. - TreeNodeBase* left_hand; ///< Left operand subtree (owned). - TreeNodeBase* right_hand; ///< Right operand subtree (owned). -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/ExpressionEvaluator.cpp b/apps/calculator/core/ExpressionEvaluator.cpp deleted file mode 100644 index 18c80604a..000000000 --- a/apps/calculator/core/ExpressionEvaluator.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/ExpressionEvaluator.cpp - * @brief Implementation of the expression evaluator facade. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "ExpressionEvaluator.h" - -#include "Parser.h" -#include "TreeNodeBase.h" - -#include - -namespace cf::desktop::desktop_component::calculator_core { - -namespace ExpressionEvaluator { - -double evalute_expression(const QString& expr) { - Parser parser; - parser.setParserString(expr); - std::unique_ptr root(parser.parse()); - return root->evaluate(); -} - -} // namespace ExpressionEvaluator - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/ExpressionEvaluator.h b/apps/calculator/core/ExpressionEvaluator.h deleted file mode 100644 index 213ff7839..000000000 --- a/apps/calculator/core/ExpressionEvaluator.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/ExpressionEvaluator.h - * @brief High-level facade that parses and evaluates an expression string. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief High-level facade for parsing and evaluating an expression. - * - * @ingroup calculator - */ -namespace ExpressionEvaluator { - -/** - * @brief Parses and evaluates @p expr in one shot. - * @param[in] expr The expression string (e.g. "1 + 2 * sin(0)"). - * @return The computed value. - * @throws Parser exceptions on malformed input or evaluation errors - * (div-by-zero, unsupported symbol/function, bad sqrt, ...). - * @since 0.20 - * @ingroup calculator - */ -double evalute_expression(const QString& expr); - -} // namespace ExpressionEvaluator - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/FunctorTreeNode.cpp b/apps/calculator/core/FunctorTreeNode.cpp deleted file mode 100644 index 26f75e2ce..000000000 --- a/apps/calculator/core/FunctorTreeNode.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/FunctorTreeNode.cpp - * @brief Implementation of FunctorTreeNode. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "FunctorTreeNode.h" - -#include "ParseExceptions.h" - -#include -#include -#include - -namespace cf::desktop::desktop_component::calculator_core { - -namespace { -/// Mapping of function name to the single-argument math lambda. -const QMap> kFunctors = { - {"sin", [](double x) { return std::sin(x); }}, - {"cos", [](double x) { return std::cos(x); }}, - {"tan", [](double x) { return std::tan(x); }}, - {"sqrt", - [](double x) { - if (x < 0) { - throw BadSqrtValue(); - } - return std::sqrt(x); - }}, - {"log", [](double x) { return std::log(x); }}, - {"exp", [](double x) { return std::exp(x); }}, -}; -} // namespace - -FunctorTreeNode::FunctorTreeNode(QString name, TreeNodeBase* arg) - : name(std::move(name)), argument(arg) {} - -FunctorTreeNode::~FunctorTreeNode() { - delete argument; -} - -double FunctorTreeNode::evaluate() const { - const double val = argument->evaluate(); - const auto it = kFunctors.find(name); - if (it != kFunctors.end()) { - return it.value()(val); - } - throw UnSupportiveFunction(name); -} - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/FunctorTreeNode.h b/apps/calculator/core/FunctorTreeNode.h deleted file mode 100644 index a306ae630..000000000 --- a/apps/calculator/core/FunctorTreeNode.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/FunctorTreeNode.h - * @brief AST node for a named function call (sin, cos, sqrt, ...). - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include "TreeNodeBase.h" -#include - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief AST node applying a named function to one argument subtree. - * - * Supports sin, cos, tan, sqrt, log, exp. Owns its argument node and - * deletes it on destruction. - * - * @ingroup calculator - */ -class FunctorTreeNode : public TreeNodeBase { - public: - /** - * @brief Constructs the node. - * @param[in] name The function name (e.g. "sin"). - * @param[in] arg The argument subtree. Ownership transfers. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - FunctorTreeNode(QString name, TreeNodeBase* arg); - - ~FunctorTreeNode() override; - - /** - * @brief Evaluates the argument and applies the function. - * @return The computed value. - * @throws UnSupportiveFunction if the name is unknown; BadSqrtValue if - * sqrt receives a negative value. - * @since 0.20 - * @ingroup calculator - */ - double evaluate() const override; - - private: - QString name; ///< The function name. - TreeNodeBase* argument; ///< The argument subtree (owned). -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/NumberNode.cpp b/apps/calculator/core/NumberNode.cpp deleted file mode 100644 index 940f193d1..000000000 --- a/apps/calculator/core/NumberNode.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/NumberNode.cpp - * @brief Implementation of NumberNode. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "NumberNode.h" - -namespace cf::desktop::desktop_component::calculator_core { - -NumberNode::NumberNode(const double val) : stored_value(val) {} - -double NumberNode::evaluate() const { - return stored_value; -} - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/NumberNode.h b/apps/calculator/core/NumberNode.h deleted file mode 100644 index 70f986828..000000000 --- a/apps/calculator/core/NumberNode.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/NumberNode.h - * @brief AST leaf node holding a numeric literal. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include "TreeNodeBase.h" - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief AST leaf node holding a numeric literal. - * - * @ingroup calculator - */ -struct NumberNode : TreeNodeBase { - /** - * @brief Constructs the node with value @p val. - * @param[in] val The numeric value to store. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - explicit NumberNode(double val); - - NumberNode() = delete; - NumberNode(const NumberNode&) = delete; - NumberNode& operator=(const NumberNode&) = delete; - NumberNode(NumberNode&&) = delete; - NumberNode& operator=(NumberNode&&) = delete; - - /** - * @brief Destructs the node. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - ~NumberNode() override = default; - - /** - * @brief Returns the stored value. - * @return The numeric value. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - double evaluate() const override; - - private: - double stored_value{0.0}; ///< The literal value. -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/ParseExceptions.h b/apps/calculator/core/ParseExceptions.h deleted file mode 100644 index 19e8da9a6..000000000 --- a/apps/calculator/core/ParseExceptions.h +++ /dev/null @@ -1,139 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/ParseExceptions.h - * @brief Exception types thrown by the calculator expression parser. - * - * Ported from CCIMXDesktop's Caculator; parse logic unchanged, wrapped in - * the calculator_core namespace. Each exception forwards its message to - * std::runtime_error, so what() returns the constructed string directly. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include -#include - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief Thrown when a division by zero is attempted. - * - * @ingroup calculator - */ -class DivideZeroException : public std::runtime_error { - public: - /** - * @brief Constructs the exception with a fixed message. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - DivideZeroException() : std::runtime_error("Divide Zero is not permitted!") {} -}; - -/** - * @brief Thrown when an unsupported symbol is encountered during parsing. - * - * @ingroup calculator - */ -class UnSupportedSymbol : public std::runtime_error { - public: - /** - * @brief Constructs the exception for @p symbol. - * @param[in] symbol The unsupported symbol. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - explicit UnSupportedSymbol(const QString& symbol) - : std::runtime_error(("Meeting unsolved symbol: " + symbol).toStdString()) {} -}; - -/** - * @brief Thrown when a numeric token cannot be parsed. - * - * @ingroup calculator - */ -class InvalidNumber : public std::runtime_error { - public: - /** - * @brief Constructs the exception with a fixed message. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - InvalidNumber() : std::runtime_error("Meeting InvalidNumber!") {} -}; - -/** - * @brief Thrown when an expression has unbalanced parentheses or structure. - * - * @ingroup calculator - */ -class UnSymmetryExpression : public std::runtime_error { - public: - /** - * @brief Constructs the exception with a fixed message. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - UnSymmetryExpression() : std::runtime_error("UnSymmetry Expression!") {} -}; - -/** - * @brief General-purpose exception for parse errors not covered by other types. - * - * @ingroup calculator - */ -class GeneralParseError : public std::runtime_error { - public: - /** - * @brief Constructs the exception with a fixed message. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - GeneralParseError() : std::runtime_error("Some Parse Error!") {} -}; - -/** - * @brief Thrown when an unsupported function name is encountered. - * - * @ingroup calculator - */ -class UnSupportiveFunction : public std::runtime_error { - public: - /** - * @brief Constructs the exception for @p function. - * @param[in] function The unsupported function name. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - explicit UnSupportiveFunction(const QString& function) - : std::runtime_error(("Meeting unsolved function: " + function).toStdString()) {} -}; - -/** - * @brief Thrown when sqrt receives a negative value. - * - * @ingroup calculator - */ -class BadSqrtValue : public std::runtime_error { - public: - /** - * @brief Constructs the exception with a fixed message. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - BadSqrtValue() : std::runtime_error("value sqrt is less then 0, which is not allowed!") {} -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/Parser.cpp b/apps/calculator/core/Parser.cpp deleted file mode 100644 index b03a04fa9..000000000 --- a/apps/calculator/core/Parser.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/Parser.cpp - * @brief Implementation of the recursive-descent parser. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "Parser.h" - -#include "BinaryOpTreeNode.h" -#include "FunctorTreeNode.h" -#include "NumberNode.h" -#include "ParseExceptions.h" -#include "UnaryOpTreeNode.h" - -namespace cf::desktop::desktop_component::calculator_core { - -void Parser::setParserString(const QString& p) { - handle_expression = p; - parse_pos = 0; -} - -QString Parser::parserString() const { - return handle_expression; -} - -TreeNodeBase* Parser::parse() { - TreeNodeBase* node = parseExpression(); - if (parse_pos < handle_expression.length()) { - throw GeneralParseError(); - } - return node; -} - -QChar Parser::peekPos() { - return parse_pos < handle_expression.length() ? handle_expression[parse_pos] : QChar(); -} - -QChar Parser::getChar() { - return parse_pos < handle_expression.length() ? handle_expression[parse_pos++] : QChar(); -} - -void Parser::skipIgnored() { - while (peekPos().isSpace()) { - ++parse_pos; - } -} - -TreeNodeBase* Parser::parseExpression() { - TreeNodeBase* node = parseTerm(); - while (true) { - skipIgnored(); - const QChar op = peekPos(); - if (op == '+' || op == '-') { - getChar(); - TreeNodeBase* rhs = parseTerm(); - node = new BinaryOpTreeNode(op, node, rhs); - } else { - break; - } - } - return node; -} - -TreeNodeBase* Parser::parseTerm() { - TreeNodeBase* node = parseFactor(); - while (true) { - skipIgnored(); - const QChar op = peekPos(); - if (op == '*' || op == '/' || op == '^') { - getChar(); - TreeNodeBase* rhs = parseFactor(); - node = new BinaryOpTreeNode(op, node, rhs); - } else { - break; - } - } - return node; -} - -TreeNodeBase* Parser::parseFactor() { - skipIgnored(); - const QChar ch = peekPos(); - - if (ch == '-') { - getChar(); - TreeNodeBase* node = parseFactor(); - return new UnaryOpTreeNode('-', node); - } - - if (ch.isLetter()) { - const QString func = parseIdentifier(); - skipIgnored(); - if (getChar() != '(') { - throw UnSymmetryExpression(); - } - TreeNodeBase* arg = parseExpression(); - skipIgnored(); - if (getChar() != ')') { - throw UnSymmetryExpression(); - } - return new FunctorTreeNode(func, arg); - } - - if (ch == '(') { - getChar(); - TreeNodeBase* node = parseExpression(); - skipIgnored(); - if (getChar() != ')') { - throw UnSymmetryExpression(); - } - return node; - } - - if (ch.isDigit() || ch == '.') { - return parseNumber(); - } - - throw UnSupportedSymbol(QString(ch)); -} - -TreeNodeBase* Parser::parseNumber() { - skipIgnored(); - const int start = parse_pos; - while (peekPos().isDigit() || peekPos() == '.') { - ++parse_pos; - } - bool ok = false; - const double val = handle_expression.mid(start, parse_pos - start).toDouble(&ok); - if (!ok) { - throw InvalidNumber(); - } - return new NumberNode(val); -} - -QString Parser::parseIdentifier() { - skipIgnored(); - const int start = parse_pos; - while (peekPos().isLetter()) { - ++parse_pos; - } - return handle_expression.mid(start, parse_pos - start); -} - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/Parser.h b/apps/calculator/core/Parser.h deleted file mode 100644 index ac918a143..000000000 --- a/apps/calculator/core/Parser.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/Parser.h - * @brief Recursive-descent parser that builds the calculator AST. - * - * Ported from CCIMXDesktop's Caculator; parse logic unchanged, wrapped in - * the calculator_core namespace. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include - -namespace cf::desktop::desktop_component::calculator_core { - -class TreeNodeBase; - -/** - * @brief Parses a mathematical expression string into an AST. - * - * Grammar (lowest to highest precedence): expression = term (('+'|'-') term)*; - * term = factor (('*'|'/'|'^') factor)*; factor = '-' factor | func '(' expr - * ')' | '(' expr ')' | number. - * - * @ingroup calculator - */ -class Parser { - public: - /** - * @brief Default-constructs the parser. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - Parser() = default; - - /** - * @brief Destructs the parser. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - ~Parser() = default; - - /** - * @brief Sets the expression string to parse. - * @param[in] p The input expression. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - void setParserString(const QString& p); - - /** - * @brief Returns the currently set expression string. - * @return The expression string. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - QString parserString() const; - - /** - * @brief Parses the full expression and returns the AST root. - * @return Pointer to the root node; caller owns the tree. - * @throws GeneralParseError on trailing input; UnSupportedSymbol, - * UnSymmetryExpression, InvalidNumber on malformed input. - * @since 0.20 - * @ingroup calculator - */ - TreeNodeBase* parse(); - - private: - /// @brief Peeks the character at the current position without advancing. - QChar peekPos(); - /// @brief Returns the current character and advances the position. - QChar getChar(); - /// @brief Skips whitespace at the current position. - void skipIgnored(); - /// @brief Parses an expression (handles +, -). - TreeNodeBase* parseExpression(); - /// @brief Parses a term (handles *, /, ^). - TreeNodeBase* parseTerm(); - /// @brief Parses a factor (unary, function, parenthesized, number). - TreeNodeBase* parseFactor(); - /// @brief Parses a numeric literal. - TreeNodeBase* parseNumber(); - /// @brief Parses an identifier (function name). - QString parseIdentifier(); - - QString handle_expression; ///< The expression being parsed. - int parse_pos{0}; ///< Current parse position. -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/TreeNodeBase.h b/apps/calculator/core/TreeNodeBase.h deleted file mode 100644 index 50106cfde..000000000 --- a/apps/calculator/core/TreeNodeBase.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/TreeNodeBase.h - * @brief Abstract base node of the calculator expression AST. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief Abstract base node of the calculator expression AST. - * - * Concrete nodes (NumberNode, BinaryOpTreeNode, UnaryOpTreeNode, - * FunctorTreeNode) implement evaluate() to compute their subtree. - * - * @ingroup calculator - */ -struct TreeNodeBase { - virtual ~TreeNodeBase() = default; - - /** - * @brief Evaluates the expression subtree. - * - * @return The computed value. - * - * @throws Parser exceptions on evaluation errors (div-by-zero, - * unsupported symbol/function, bad sqrt, etc.). - * - * @since 0.20 - * @ingroup calculator - */ - virtual double evaluate() const = 0; -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/UnaryOpTreeNode.cpp b/apps/calculator/core/UnaryOpTreeNode.cpp deleted file mode 100644 index 9a952ba15..000000000 --- a/apps/calculator/core/UnaryOpTreeNode.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/UnaryOpTreeNode.cpp - * @brief Implementation of UnaryOpTreeNode. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "UnaryOpTreeNode.h" - -namespace cf::desktop::desktop_component::calculator_core { - -UnaryOpTreeNode::UnaryOpTreeNode(const QChar oper, TreeNodeBase* child) - : op(oper), operand(child) {} - -UnaryOpTreeNode::~UnaryOpTreeNode() { - delete operand; -} - -double UnaryOpTreeNode::evaluate() const { - const double val = operand->evaluate(); - if (op == '-') { - return -val; - } - throw UnSupportedSymbol(QString(op)); -} - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/core/UnaryOpTreeNode.h b/apps/calculator/core/UnaryOpTreeNode.h deleted file mode 100644 index 42d580e29..000000000 --- a/apps/calculator/core/UnaryOpTreeNode.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file desktop/ui/components/builtin_apps/calculator/core/UnaryOpTreeNode.h - * @brief AST node for a unary operation (negation). - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#pragma once - -#include "ParseExceptions.h" -#include "TreeNodeBase.h" -#include - -namespace cf::desktop::desktop_component::calculator_core { - -/** - * @brief AST node applying a unary operator to one subtree. - * - * Currently only negation ('-') is supported. Owns its operand and deletes - * it on destruction. - * - * @ingroup calculator - */ -class UnaryOpTreeNode : public TreeNodeBase { - public: - /** - * @brief Constructs the node. - * @param[in] oper The operator character (only '-' is supported). - * @param[in] child The operand subtree. Ownership transfers. - * @throws None. - * @since 0.20 - * @ingroup calculator - */ - UnaryOpTreeNode(QChar oper, TreeNodeBase* child); - - ~UnaryOpTreeNode() override; - - /** - * @brief Evaluates the operand and applies the unary operator. - * @return The computed value. - * @throws UnSupportedSymbol if the operator is not '-'. - * @since 0.20 - * @ingroup calculator - */ - double evaluate() const override; - - private: - QChar op; ///< The operator character. - TreeNodeBase* operand; ///< The operand subtree (owned). -}; - -} // namespace cf::desktop::desktop_component::calculator_core diff --git a/apps/calculator/main.cpp b/apps/calculator/main.cpp deleted file mode 100644 index f8dc085bf..000000000 --- a/apps/calculator/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file apps/calculator/main.cpp - * @brief Standalone Calculator application entry point. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "calculator_panel.h" - -#include -#include - -int main(int argc, char* argv[]) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("CFDesktop Calculator")); - - cf::desktop::desktop_component::CalculatorPanel panel; - panel.setWindowTitle(QStringLiteral("Calculator")); - panel.resize(360, 520); - panel.show(); - - return app.exec(); -} diff --git a/apps/calendar/CMakeLists.txt b/apps/calendar/CMakeLists.txt deleted file mode 100644 index 028a922ea..000000000 --- a/apps/calendar/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# Calendar standalone application. -# Ported from CCIMXDesktop's CCCalendar; UI uses QuarkWidgets MD3 widgets plus -# Qt's QCalendarWidget for the month grid (MVP — a self-drawn MD3 calendar may -# replace it later). -# CFDesktop launches this binary via AppLaunchService (QProcess), so it runs in -# its own process (isolated from the desktop shell). - -# Standalone Calendar executable (consumed by CFDesktop via QProcess launch). -qt_add_executable(calendar - main.cpp - calendar_panel.cpp -) - -target_link_libraries(calendar PRIVATE - QuarkWidgets::quarkwidgets - Qt6::Widgets -) - -# App package is self-contained under /../apps/calendar/ (executable + -# manifest in the same dir). AppDiscoverer scans /../apps//app.json. -set(CALENDAR_APP_DIR "${CMAKE_BINARY_DIR}/apps/calendar") -set_target_properties(calendar PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CALENDAR_APP_DIR}" -) - -# Deploy the manifest next to the executable (at configure time). -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app.json" "${CALENDAR_APP_DIR}/app.json" COPYONLY) diff --git a/apps/calendar/app.json b/apps/calendar/app.json deleted file mode 100644 index 88429f8e3..000000000 --- a/apps/calendar/app.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "app_id": "calendar", - "display_name": "Calendar", - "exec": "calendar", - "launch_kind": "auto" -} diff --git a/apps/calendar/calendar_panel.cpp b/apps/calendar/calendar_panel.cpp deleted file mode 100644 index 39e4c4640..000000000 --- a/apps/calendar/calendar_panel.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file apps/calendar/calendar_panel.cpp - * @brief Implementation of the Calendar panel. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup calendar - */ - -#include "calendar_panel.h" - -#include "ui/widget/material/widget/button/button.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace cf::desktop::desktop_component { - -using qw::widget::material::Button; -using Variant = Button::ButtonVariant; - -namespace { -constexpr qreal kCornerRadius = 16.0; ///< Card corner radius (px). -} // namespace - -CalendarPanel::CalendarPanel(QWidget* parent) : QWidget(parent) { - setupUi(); - refreshDateDescription(); -} - -CalendarPanel::~CalendarPanel() = default; - -void CalendarPanel::setupUi() { - auto* root = new QVBoxLayout(this); - root->setContentsMargins(12, 12, 12, 12); - root->setSpacing(8); - - // Top split: calendar (left) | note editor (right). - auto* split = new QHBoxLayout; - split->setSpacing(8); - - calendar_ = new QCalendarWidget(this); - split->addWidget(calendar_, /*stretch=*/3); - - auto* editor_col = new QVBoxLayout; - editor_col->setSpacing(6); - - date_label_ = new QLabel(this); - date_label_->setWordWrap(true); - editor_col->addWidget(date_label_); - - editor_ = new QTextEdit(this); - editor_->setPlaceholderText(QStringLiteral("Write a note for the selected date...")); - editor_col->addWidget(editor_); - - auto* toolbar = new QHBoxLayout; - toolbar->setSpacing(6); - auto* save_btn = new Button(QStringLiteral("Save"), Variant::Filled, this); - auto* delete_btn = new Button(QStringLiteral("Delete"), Variant::Outlined, this); - toolbar->addWidget(save_btn); - toolbar->addWidget(delete_btn); - toolbar->addStretch(); - editor_col->addLayout(toolbar); - - split->addLayout(editor_col, /*stretch=*/2); - root->addLayout(split); - - connect(calendar_, &QCalendarWidget::selectionChanged, this, - &CalendarPanel::onSelectionChanged); - connect(save_btn, &QPushButton::clicked, this, &CalendarPanel::onSaveNote); - connect(delete_btn, &QPushButton::clicked, this, &CalendarPanel::onDeleteNote); -} - -void CalendarPanel::onSelectionChanged() { - const QDate date = calendar_->selectedDate(); - editor_->setPlainText(notes_.value(date)); - refreshDateDescription(); -} - -void CalendarPanel::onSaveNote() { - const QDate date = calendar_->selectedDate(); - const QString text = editor_->toPlainText(); - if (text.isEmpty()) { - notes_.remove(date); - } else { - notes_.insert(date, text); - } -} - -void CalendarPanel::onDeleteNote() { - const QDate date = calendar_->selectedDate(); - notes_.remove(date); - editor_->clear(); -} - -void CalendarPanel::refreshDateDescription() { - date_label_->setText(describeDate(calendar_->selectedDate())); -} - -QString CalendarPanel::describeDate(const QDate& date) { - if (!date.isValid()) { - return QStringLiteral("Invalid date"); - } - return QStringLiteral("%1 %2").arg( - date.toString(Qt::TextDate), - QStringLiteral("(day %1 of %2)").arg(date.dayOfYear()).arg(date.daysInYear())); -} - -void CalendarPanel::paintEvent(QPaintEvent* /*event*/) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing, true); - const QColor surface_color(0xF7, 0xF5, 0xF3); - QPainterPath card; - card.addRoundedRect(QRectF(rect()), kCornerRadius, kCornerRadius); - p.fillPath(card, surface_color); -} - -} // namespace cf::desktop::desktop_component diff --git a/apps/calendar/calendar_panel.h b/apps/calendar/calendar_panel.h deleted file mode 100644 index 9a907dbbb..000000000 --- a/apps/calendar/calendar_panel.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @file apps/calendar/calendar_panel.h - * @brief Calendar main panel (standalone app). - * - * Root widget of the standalone Calendar executable. Renders a month calendar - * (Qt QCalendarWidget, MVP) with a side panel that shows and edits notes bound - * to the selected date. Notes are kept in an in-memory map for the MVP; - * persistence is deferred (see TODO in the .cpp). Ported from CCIMXDesktop's - * CCCalendar, with the original QMainWindow + .ui layout rewritten as a plain - * QWidget using QuarkWidgets MD3 buttons. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup calendar - */ - -#pragma once - -#include -#include -#include -#include - -class QCalendarWidget; -class QLabel; -class QPaintEvent; -class QTextEdit; - -namespace qw::widget::material { -class Button; -} - -namespace cf::desktop::desktop_component { - -/** - * @brief Root widget of the standalone Calendar application. - * - * @ingroup calendar - */ -class CalendarPanel final : public QWidget { - Q_OBJECT - public: - /** - * @brief Constructs the Calendar panel. - * - * @param[in] parent Parent widget (nullptr for a top-level window). - * - * @throws None - * @since 0.20 - * @ingroup calendar - */ - explicit CalendarPanel(QWidget* parent = nullptr); - - /** - * @brief Destructs the panel. - * - * @throws None - * @since 0.20 - * @ingroup calendar - */ - ~CalendarPanel() override; - - protected: - /** - * @brief Paints the rounded Material card background. - * - * @param[in] event The paint event descriptor. - * - * @throws None - * @since 0.20 - * @ingroup calendar - */ - void paintEvent(QPaintEvent* event) override; - - private slots: - /// @brief Loads the selected date's note into the editor and updates the - /// date description label. - void onSelectionChanged(); - /// @brief Commits the editor text back into the note store for the current - /// date. - void onSaveNote(); - /// @brief Removes the current date's note and clears the editor. - void onDeleteNote(); - - private: - /// @brief Builds the calendar + editor layout. - void setupUi(); - /// @brief Refreshes the date description label for the selected date. - void refreshDateDescription(); - /// @brief Returns a human-readable description of @p date. - static QString describeDate(const QDate& date); - - QCalendarWidget* calendar_{nullptr}; ///< Month calendar (MVP). - QTextEdit* editor_{nullptr}; ///< Note editor for the selected date. - QLabel* date_label_{nullptr}; ///< Selected-date read-out / description. - - /// @brief In-memory note store: selected date -> note text. - /// - /// TODO: replace with persistent storage (JSON file under the user data - /// dir, or desktop ConfigStore) in a follow-up. Lost on app exit today. - QMap notes_; -}; - -} // namespace cf::desktop::desktop_component diff --git a/apps/calendar/main.cpp b/apps/calendar/main.cpp deleted file mode 100644 index b1662ca43..000000000 --- a/apps/calendar/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file apps/calendar/main.cpp - * @brief Standalone Calendar application entry point. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup calendar - */ - -#include "calendar_panel.h" - -#include -#include - -int main(int argc, char* argv[]) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("CFDesktop Calendar")); - - cf::desktop::desktop_component::CalendarPanel panel; - panel.setWindowTitle(QStringLiteral("Calendar")); - panel.resize(720, 480); - panel.show(); - - return app.exec(); -} diff --git a/apps/noter/CMakeLists.txt b/apps/noter/CMakeLists.txt deleted file mode 100644 index 3ddaafd60..000000000 --- a/apps/noter/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# Noter standalone application. -# Ported from CCIMXDesktop's CCIMXNoter; UI uses QuarkWidgets MD3 widgets. -# CFDesktop launches this binary via AppLaunchService (QProcess), so it runs in -# its own process (isolated from the desktop shell). - -# Standalone Noter executable (consumed by CFDesktop via QProcess launch). -qt_add_executable(noter - main.cpp - noter_panel.cpp -) - -target_link_libraries(noter PRIVATE - QuarkWidgets::quarkwidgets - Qt6::Widgets -) - -# App package is self-contained under /../apps/noter/ (executable + -# manifest in the same dir). AppDiscoverer scans /../apps//app.json. -set(NOTER_APP_DIR "${CMAKE_BINARY_DIR}/apps/noter") -set_target_properties(noter PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${NOTER_APP_DIR}" -) - -# Deploy the manifest next to the executable (at configure time). -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app.json" "${NOTER_APP_DIR}/app.json" COPYONLY) diff --git a/apps/noter/app.json b/apps/noter/app.json deleted file mode 100644 index 7335e2d41..000000000 --- a/apps/noter/app.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "app_id": "noter", - "display_name": "Noter", - "exec": "noter", - "launch_kind": "auto" -} diff --git a/apps/noter/main.cpp b/apps/noter/main.cpp deleted file mode 100644 index 83b8d0e2c..000000000 --- a/apps/noter/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file apps/noter/main.cpp - * @brief Standalone Noter application entry point. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup noter - */ - -#include "noter_panel.h" - -#include -#include - -int main(int argc, char* argv[]) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("CFDesktop Noter")); - - cf::desktop::desktop_component::NoterPanel panel; - panel.setWindowTitle(QStringLiteral("Noter")); - panel.resize(480, 560); - panel.show(); - - return app.exec(); -} diff --git a/apps/noter/noter_panel.cpp b/apps/noter/noter_panel.cpp deleted file mode 100644 index 46e7060e4..000000000 --- a/apps/noter/noter_panel.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @file apps/noter/noter_panel.cpp - * @brief Implementation of the Noter panel. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup noter - */ - -#include "noter_panel.h" - -#include "ui/widget/material/widget/button/button.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cf::desktop::desktop_component { - -using qw::widget::material::Button; -using Variant = Button::ButtonVariant; - -namespace { -constexpr qreal kCornerRadius = 16.0; ///< Card corner radius (px). -constexpr int kMinFontSize = 8; ///< Minimum font size (pt). -constexpr int kMaxFontSize = 40; ///< Maximum font size (pt). -constexpr int kDefaultFontSize = 15; ///< Initial font size (pt). -} // namespace - -NoterPanel::NoterPanel(QWidget* parent) : QWidget(parent) { - setupUi(); -} - -NoterPanel::~NoterPanel() = default; - -void NoterPanel::setupUi() { - auto* layout = new QVBoxLayout(this); - layout->setContentsMargins(12, 12, 12, 12); - layout->setSpacing(8); - - // Toolbar: Open | Save | B | I | size slider | size readout. - auto* toolbar = new QHBoxLayout; - toolbar->setSpacing(8); - - auto* open_btn = new Button(QStringLiteral("Open"), Variant::Outlined, this); - auto* save_btn = new Button(QStringLiteral("Save"), Variant::Outlined, this); - auto* bold_btn = new Button(QStringLiteral("B"), Variant::Tonal, this); - bold_btn->setCheckable(true); - auto* italic_btn = new Button(QStringLiteral("I"), Variant::Tonal, this); - italic_btn->setCheckable(true); - - font_slider_ = new QSlider(Qt::Horizontal, this); - font_slider_->setMinimum(kMinFontSize); - font_slider_->setMaximum(kMaxFontSize); - font_slider_->setValue(kDefaultFontSize); - font_slider_->setFixedWidth(120); - - size_label_ = new QLabel(QString::number(kDefaultFontSize), this); - size_label_->setFixedWidth(28); - - toolbar->addWidget(open_btn); - toolbar->addWidget(save_btn); - toolbar->addWidget(bold_btn); - toolbar->addWidget(italic_btn); - toolbar->addWidget(font_slider_); - toolbar->addWidget(size_label_); - toolbar->addStretch(); - layout->addLayout(toolbar); - - // Editor. - editor_ = new QTextEdit(this); - editor_->setFontPointSize(kDefaultFontSize); - layout->addWidget(editor_); - - connect(open_btn, &QPushButton::clicked, this, &NoterPanel::onOpen); - connect(save_btn, &QPushButton::clicked, this, &NoterPanel::onSave); - connect(font_slider_, &QSlider::valueChanged, this, &NoterPanel::onFontSizeChanged); - connect(bold_btn, &QPushButton::toggled, this, &NoterPanel::onBoldToggled); - connect(italic_btn, &QPushButton::toggled, this, &NoterPanel::onItalicToggled); -} - -void NoterPanel::applyCharFormat(const QTextCharFormat& format) { - QTextCursor cursor = editor_->textCursor(); - if (!cursor.hasSelection()) { - cursor.select(QTextCursor::WordUnderCursor); - } - cursor.mergeCharFormat(format); - editor_->mergeCurrentCharFormat(format); -} - -void NoterPanel::onOpen() { - const QString file_name = QFileDialog::getOpenFileName(this, tr("Open File")); - if (file_name.isEmpty()) { - return; - } - QFile file(file_name); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&file); - editor_->setPlainText(in.readAll()); - } -} - -void NoterPanel::onSave() { - const QString file_name = QFileDialog::getSaveFileName(this, tr("Save File")); - if (file_name.isEmpty()) { - return; - } - QFile file(file_name); - if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QTextStream out(&file); - out << editor_->toPlainText(); - } -} - -void NoterPanel::onFontSizeChanged(int size) { - size_label_->setText(QString::number(size)); - QTextCharFormat format; - format.setFontPointSize(size); - applyCharFormat(format); -} - -void NoterPanel::onBoldToggled(bool checked) { - QTextCharFormat format; - format.setFontWeight(checked ? QFont::Bold : QFont::Normal); - applyCharFormat(format); -} - -void NoterPanel::onItalicToggled(bool checked) { - QTextCharFormat format; - format.setFontItalic(checked); - applyCharFormat(format); -} - -void NoterPanel::paintEvent(QPaintEvent* /*event*/) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing, true); - const QColor surface_color(0xF7, 0xF5, 0xF3); - QPainterPath card; - card.addRoundedRect(QRectF(rect()), kCornerRadius, kCornerRadius); - p.fillPath(card, surface_color); -} - -} // namespace cf::desktop::desktop_component diff --git a/apps/noter/noter_panel.h b/apps/noter/noter_panel.h deleted file mode 100644 index ca147034f..000000000 --- a/apps/noter/noter_panel.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @file apps/noter/noter_panel.h - * @brief Noter main panel (standalone app). - * - * Root widget of the standalone Noter executable. Plain-text editor with - * basic font formatting (size/bold/italic) and open/save, rendered with - * QuarkWidgets MD3 buttons. Ported from CCIMXDesktop's CCIMXNoter. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup noter - */ - -#pragma once - -#include - -class QKeyEvent; -class QLabel; -class QPaintEvent; -class QSlider; -class QTextCharFormat; -class QTextEdit; - -namespace qw::widget::material { -class Button; -} - -namespace cf::desktop::desktop_component { - -/** - * @brief Root widget of the standalone Noter application. - * - * @ingroup noter - */ -class NoterPanel final : public QWidget { - Q_OBJECT - public: - /** - * @brief Constructs the Noter panel. - * - * @param[in] parent Parent widget (nullptr for a top-level window). - * - * @throws None - * @since 0.20 - * @ingroup noter - */ - explicit NoterPanel(QWidget* parent = nullptr); - - /** - * @brief Destructs the panel. - * - * @throws None - * @since 0.20 - * @ingroup noter - */ - ~NoterPanel() override; - - protected: - /** - * @brief Paints the rounded Material card background. - * - * @param[in] event The paint event descriptor. - * - * @throws None - * @since 0.20 - * @ingroup noter - */ - void paintEvent(QPaintEvent* event) override; - - private slots: - /// @brief Opens a file dialog and loads text into the editor. - void onOpen(); - /// @brief Opens a file dialog and saves the editor text. - void onSave(); - /// @brief Applies the slider font size to the current selection. - void onFontSizeChanged(int size); - /// @brief Toggles bold on the current selection. - void onBoldToggled(bool checked); - /// @brief Toggles italic on the current selection. - void onItalicToggled(bool checked); - - private: - /// @brief Builds the toolbar + editor layout. - void setupUi(); - /// @brief Merges a char format onto the current word or selection. - void applyCharFormat(const QTextCharFormat& format); - - QTextEdit* editor_{nullptr}; ///< Plain-text edit area. - QSlider* font_slider_{nullptr}; ///< Font size slider (8..40). - QLabel* size_label_{nullptr}; ///< Current font size readout. -}; - -} // namespace cf::desktop::desktop_component diff --git a/apps/system_state/CMakeLists.txt b/apps/system_state/CMakeLists.txt deleted file mode 100644 index c28ffd835..000000000 --- a/apps/system_state/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# System State standalone application. -# Ported from CCIMXDesktop's SystemState. Unlike the original (which shipped -# its own platform-specific CPU/memory fetchers), this version reuses the -# mature base/ probes (cfbase) for system data — showcasing the base-layer -# capability. The UI is rebuilt with QuarkWidgets MD3 widgets + plain Qt -# labels (no QtCharts dependency, no .ui files). -# CFDesktop launches this binary via AppLaunchService (QProcess), so it runs -# in its own process (isolated from the desktop shell). - -# Standalone System State executable (consumed by CFDesktop via QProcess). -qt_add_executable(system_state - main.cpp - system_state_panel.cpp -) - -target_link_libraries(system_state PRIVATE - cfbase - QuarkWidgets::quarkwidgets - Qt6::Widgets -) - -# App package is self-contained under /../apps/system_state/ (executable + -# manifest in the same dir). AppDiscoverer scans /../apps//app.json, -# resolves exec to the co-located absolute path, and AppLaunchService launches -# it directly — no working-directory dependency, no PATH search needed. -set(SYSTEM_STATE_APP_DIR "${CMAKE_BINARY_DIR}/apps/system_state") -set_target_properties(system_state PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${SYSTEM_STATE_APP_DIR}" -) - -# Deploy the manifest next to the executable (at configure time). -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app.json" "${SYSTEM_STATE_APP_DIR}/app.json" COPYONLY) diff --git a/apps/system_state/app.json b/apps/system_state/app.json deleted file mode 100644 index f3388fd4a..000000000 --- a/apps/system_state/app.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "app_id": "system_state", - "display_name": "System State", - "exec": "system_state", - "launch_kind": "auto" -} diff --git a/apps/system_state/main.cpp b/apps/system_state/main.cpp deleted file mode 100644 index f24d64cdc..000000000 --- a/apps/system_state/main.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file apps/system_state/main.cpp - * @brief Standalone System State application entry point. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup system_state - */ - -#include "system_state_panel.h" - -#include -#include - -int main(int argc, char* argv[]) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("CFDesktop System State")); - - cf::desktop::desktop_component::SystemStatePanel panel; - panel.setWindowTitle(QStringLiteral("System State")); - panel.resize(520, 640); - panel.show(); - - return app.exec(); -} diff --git a/apps/system_state/system_state_panel.cpp b/apps/system_state/system_state_panel.cpp deleted file mode 100644 index 8ccb8689d..000000000 --- a/apps/system_state/system_state_panel.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/** - * @file apps/system_state/system_state_panel.cpp - * @brief Implementation of the System State panel. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup system_state - */ - -#include "system_state_panel.h" - -#include "ui/widget/material/widget/button/button.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "system/cpu/cfcpu.h" -#include "system/cpu/cfcpu_bonus.h" -#include "system/cpu/cfcpu_profile.h" -#include "system/memory/memory_info.h" - -namespace cf::desktop::desktop_component { - -using qw::widget::material::Button; -using Variant = Button::ButtonVariant; - -namespace { -constexpr qreal kCornerRadius = 16.0; ///< Card corner radius (px). -constexpr int kRefreshIntervalMs = 2000; ///< Auto-refresh interval (ms). - -/// @brief Formats a byte count into a human-readable binary string. -/// @param bytes The size in bytes. -/// @return String like "8.00 GB", "512.00 MB", or "1.23 KB". -QString formatBytes(uint64_t bytes) { - constexpr uint64_t kb = 1024ULL; - constexpr uint64_t mb = kb * 1024ULL; - constexpr uint64_t gb = mb * 1024ULL; - if (bytes < kb) { - return QStringLiteral("%1 B").arg(static_cast(bytes)); - } - if (bytes < mb) { - return QString::number(static_cast(bytes) / kb, 'f', 2) + " KB"; - } - if (bytes < gb) { - return QString::number(static_cast(bytes) / mb, 'f', 2) + " MB"; - } - return QString::number(static_cast(bytes) / gb, 'f', 2) + " GB"; -} - -/// @brief Builds a section heading label. -/// @param title The heading text. -/// @return A styled, bold label. -QLabel* makeSectionLabel(const QString& title) { - auto* label = new QLabel(title); - QFont font = label->font(); - font.setBold(true); - font.setPointSize(font.pointSize() + 1); - label->setFont(font); - return label; -} - -/// @brief Builds a two-column key/value row. -/// @param key The left column caption. -/// @return The value label (caller updates its text). -QLabel* makeRow(QVBoxLayout* layout, const QString& key) { - auto* row = new QHBoxLayout; - row->setSpacing(12); - auto* key_label = new QLabel(key); - key_label->setMinimumWidth(170); - auto* value_label = new QLabel(QStringLiteral("—")); - value_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - value_label->setTextInteractionFlags(Qt::TextSelectableByMouse); - row->addWidget(key_label); - row->addWidget(value_label, 1); - layout->addLayout(row); - return value_label; -} -} // namespace - -SystemStatePanel::SystemStatePanel(QWidget* parent) : QWidget(parent) { - setupUi(); - loadStaticInfo(); - loadLiveInfo(); - - refresh_timer_ = new QTimer(this); - refresh_timer_->setInterval(kRefreshIntervalMs); - connect(refresh_timer_, &QTimer::timeout, this, &SystemStatePanel::loadLiveInfo); - refresh_timer_->start(); -} - -SystemStatePanel::~SystemStatePanel() = default; - -void SystemStatePanel::setupUi() { - auto* outer = new QVBoxLayout(this); - outer->setContentsMargins(12, 12, 12, 12); - outer->setSpacing(8); - - // Toolbar: Refresh | Auto (toggle). - auto* toolbar = new QHBoxLayout; - toolbar->setSpacing(8); - auto* refresh_btn = new Button(QStringLiteral("Refresh"), Variant::Outlined, this); - auto* auto_btn = new Button(QStringLiteral("Auto"), Variant::Tonal, this); - auto_btn->setCheckable(true); - auto_btn->setChecked(true); - toolbar->addWidget(refresh_btn); - toolbar->addWidget(auto_btn); - toolbar->addStretch(); - outer->addLayout(toolbar); - - // Scrollable readout area. - auto* scroll = new QScrollArea(this); - scroll->setWidgetResizable(true); - auto* content = new QWidget; - auto* layout = new QVBoxLayout(content); - layout->setContentsMargins(8, 8, 8, 8); - layout->setSpacing(4); - - // CPU static section. - layout->addWidget(makeSectionLabel(QStringLiteral("CPU"))); - cpu_model_label_ = makeRow(layout, QStringLiteral("Model")); - cpu_arch_label_ = makeRow(layout, QStringLiteral("Architecture")); - cpu_manufacturer_label_ = makeRow(layout, QStringLiteral("Manufacturer")); - cpu_cores_label_ = makeRow(layout, QStringLiteral("Cores (logical/physical)")); - cpu_freq_label_ = makeRow(layout, QStringLiteral("Frequency (current/max)")); - cpu_usage_label_ = makeRow(layout, QStringLiteral("Usage")); - cpu_temp_label_ = makeRow(layout, QStringLiteral("Temperature")); - - layout->addSpacing(8); - - // Memory section. - layout->addWidget(makeSectionLabel(QStringLiteral("Memory"))); - mem_phys_total_label_ = makeRow(layout, QStringLiteral("Physical total")); - mem_phys_used_label_ = makeRow(layout, QStringLiteral("Physical used")); - mem_phys_avail_label_ = makeRow(layout, QStringLiteral("Physical available")); - mem_swap_total_label_ = makeRow(layout, QStringLiteral("Swap total")); - mem_swap_used_label_ = makeRow(layout, QStringLiteral("Swap used")); - - layout->addStretch(); - scroll->setWidget(content); - outer->addWidget(scroll, 1); - - connect(refresh_btn, &QPushButton::clicked, this, &SystemStatePanel::refreshNow); - connect(auto_btn, &QPushButton::toggled, this, &SystemStatePanel::toggleAutoRefresh); -} - -void SystemStatePanel::loadStaticInfo() { - const auto info_result = cf::getCPUInfo(); - if (info_result.has_value()) { - const auto& info = info_result.value(); - cpu_model_label_->setText(QString::fromStdString(std::string(info.model))); - cpu_arch_label_->setText(QString::fromStdString(std::string(info.arch))); - cpu_manufacturer_label_->setText(QString::fromStdString(std::string(info.manufacturer))); - } else { - cpu_model_label_->setText(QStringLiteral("Unavailable")); - cpu_arch_label_->setText(QStringLiteral("Unavailable")); - cpu_manufacturer_label_->setText(QStringLiteral("Unavailable")); - } -} - -void SystemStatePanel::loadLiveInfo() { - // CPU profile (cores / frequency / usage) — real-time each call. - const auto profile_result = cf::getCPUProfileInfo(); - if (profile_result.has_value()) { - const auto& p = profile_result.value(); - cpu_cores_label_->setText(QStringLiteral("%1 / %2").arg(p.logical_cnt).arg(p.physical_cnt)); - cpu_freq_label_->setText( - QStringLiteral("%1 / %2 MHz").arg(p.current_frequecy).arg(p.max_frequency)); - cpu_usage_label_->setText(QString::number(p.cpu_usage_percentage, 'f', 1) + " %"); - } else { - cpu_cores_label_->setText(QStringLiteral("Unavailable")); - cpu_freq_label_->setText(QStringLiteral("Unavailable")); - cpu_usage_label_->setText(QStringLiteral("Unavailable")); - } - - // CPU bonus (temperature) — cached, refreshed on demand. - const auto bonus_result = cf::getCPUBonusInfo(true); - if (bonus_result.has_value() && bonus_result.value().temperature.has_value()) { - cpu_temp_label_->setText(QString::number(*bonus_result.value().temperature) + " C"); - } else { - cpu_temp_label_->setText(QStringLiteral("Unavailable")); - } - - // Memory — real-time each call. - cf::MemoryInfo mem{}; - cf::getSystemMemoryInfo(mem); - - const uint64_t phys_total = mem.physical.total_bytes; - const uint64_t phys_avail = mem.physical.available_bytes; - const uint64_t phys_used = (phys_total > phys_avail) ? (phys_total - phys_avail) : 0; - mem_phys_total_label_->setText(formatBytes(phys_total)); - mem_phys_avail_label_->setText(formatBytes(phys_avail)); - if (phys_total > 0) { - const double phys_pct = static_cast(phys_used) / phys_total * 100.0; - mem_phys_used_label_->setText( - QStringLiteral("%1 (%2%)").arg(formatBytes(phys_used)).arg(phys_pct, 0, 'f', 1)); - } else { - mem_phys_used_label_->setText(formatBytes(phys_used)); - } - - const uint64_t swap_total = mem.swap.total_bytes; - const uint64_t swap_free = mem.swap.free_bytes; - const uint64_t swap_used = (swap_total > swap_free) ? (swap_total - swap_free) : 0; - mem_swap_total_label_->setText(formatBytes(swap_total)); - if (swap_total > 0) { - const double swap_pct = static_cast(swap_used) / swap_total * 100.0; - mem_swap_used_label_->setText( - QStringLiteral("%1 (%2%)").arg(formatBytes(swap_used)).arg(swap_pct, 0, 'f', 1)); - } else { - mem_swap_used_label_->setText(formatBytes(swap_used)); - } -} - -void SystemStatePanel::refreshNow() { - loadStaticInfo(); - loadLiveInfo(); -} - -void SystemStatePanel::toggleAutoRefresh(bool checked) { - checked ? refresh_timer_->start() : refresh_timer_->stop(); -} - -void SystemStatePanel::paintEvent(QPaintEvent* /*event*/) { - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing, true); - const QColor surface_color(0xF7, 0xF5, 0xF3); - QPainterPath card; - card.addRoundedRect(QRectF(rect()), kCornerRadius, kCornerRadius); - p.fillPath(card, surface_color); -} - -} // namespace cf::desktop::desktop_component diff --git a/apps/system_state/system_state_panel.h b/apps/system_state/system_state_panel.h deleted file mode 100644 index d39fd80c5..000000000 --- a/apps/system_state/system_state_panel.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @file apps/system_state/system_state_panel.h - * @brief System State main panel (standalone app). - * - * Root widget of the standalone System State executable. Surveys live system - * telemetry (CPU model/cores/frequency/usage/temperature, physical and swap - * memory) and renders it as a scrollable Material card with QuarkWidgets MD3 - * buttons (refresh/pause). Ported from CCIMXDesktop's SystemState. - * - * Unlike the CCIMX original (which bundled its own platform-specific CPU and - * memory fetchers plus QtCharts), this port reuses the mature base/ probes - * (cf::getCPUInfo, cf::getCPUBonusInfo, cf::getCPUProfileInfo, - * cf::getSystemMemoryInfo) and replaces the charts with plain text readouts, - * so it depends only on cfbase + QuarkWidgets + Qt Widgets. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-07-01 - * @version 0.1 - * @since 0.20 - * @ingroup system_state - */ - -#pragma once - -#include - -class QLabel; -class QPaintEvent; -class QTimer; - -namespace cf::desktop::desktop_component { - -/** - * @brief Root widget of the standalone System State application. - * - * @ingroup system_state - */ -class SystemStatePanel final : public QWidget { - Q_OBJECT - public: - /** - * @brief Constructs the System State panel. - * - * @param[in] parent Parent widget (nullptr for a top-level window). - * - * @throws None - * @since 0.20 - * @ingroup system_state - */ - explicit SystemStatePanel(QWidget* parent = nullptr); - - /** - * @brief Destructs the panel. - * - * @throws None - * @since 0.20 - * @ingroup system_state - */ - ~SystemStatePanel() override; - - protected: - /** - * @brief Paints the rounded Material card background. - * - * @param[in] event The paint event descriptor. - * - * @throws None - * @since 0.20 - * @ingroup system_state - */ - void paintEvent(QPaintEvent* event) override; - - private slots: - /// @brief Performs a one-shot refresh of all telemetry readouts. - void refreshNow(); - /// @brief Toggles the periodic auto-refresh timer on or off. - void toggleAutoRefresh(bool checked); - - private: - /// @brief Builds the toolbar + readout layout. - void setupUi(); - /// @brief Queries the base/ probes once and fills the static labels. - void loadStaticInfo(); - /// @brief Queries the base/ probes and updates the live labels. - void loadLiveInfo(); - - QTimer* refresh_timer_{nullptr}; ///< Periodic auto-refresh timer. - - // Static (cached) CPU info labels. - QLabel* cpu_model_label_{nullptr}; ///< CPU model name. - QLabel* cpu_arch_label_{nullptr}; ///< CPU architecture. - QLabel* cpu_manufacturer_label_{nullptr}; ///< CPU manufacturer. - - // Live CPU labels. - QLabel* cpu_cores_label_{nullptr}; ///< Logical/physical core counts. - QLabel* cpu_freq_label_{nullptr}; ///< Current/max frequency (MHz). - QLabel* cpu_usage_label_{nullptr}; ///< Current CPU usage (%). - QLabel* cpu_temp_label_{nullptr}; ///< CPU temperature (C). - - // Live memory labels. - QLabel* mem_phys_total_label_{nullptr}; ///< Total physical memory. - QLabel* mem_phys_used_label_{nullptr}; ///< Used physical memory + %. - QLabel* mem_phys_avail_label_{nullptr}; ///< Available physical memory. - QLabel* mem_swap_total_label_{nullptr}; ///< Total swap memory. - QLabel* mem_swap_used_label_{nullptr}; ///< Used swap memory + %. -}; - -} // namespace cf::desktop::desktop_component diff --git a/test/desktop/CMakeLists.txt b/test/desktop/CMakeLists.txt index 7c095a734..d0ef483f5 100644 --- a/test/desktop/CMakeLists.txt +++ b/test/desktop/CMakeLists.txt @@ -13,9 +13,6 @@ add_subdirectory(window_placement) # Add wallpaper animation tests subdirectory add_subdirectory(wallpaper_animation) -# Add calculator parser tests subdirectory -add_subdirectory(calculator) - # Add launcher (app discovery) tests subdirectory add_subdirectory(launcher) diff --git a/test/desktop/calculator/CMakeLists.txt b/test/desktop/calculator/CMakeLists.txt deleted file mode 100644 index 8f0942621..000000000 --- a/test/desktop/calculator/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Calculator parser unit tests (ported from CCIMXDesktop's test_parser). -add_gtest_executable( - TEST_NAME parser_test - SOURCE_FILE parser_test.cpp - LINK_LIBRARIES cfdesktop_calculator_parser;Qt6::Core;GTest::gtest;GTest::gtest_main - LABELS "desktop;unit;calculator" - LOG_MODULE calculator_tests -) diff --git a/test/desktop/calculator/parser_test.cpp b/test/desktop/calculator/parser_test.cpp deleted file mode 100644 index f779538fc..000000000 --- a/test/desktop/calculator/parser_test.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @file test/desktop/calculator/parser_test.cpp - * @brief GoogleTest port of CCIMXDesktop's Caculator test_parser. - * - * Covers the same cases as the upstream test_parser.cpp (basic arithmetic, - * precedence, unary negation, functions, nesting, edge cases, and error - * throws) to confirm the port is behaviorally equivalent. - * - * @author Charliechen114514 (chengh1922@mails.jlu.edu.cn) - * @date 2026-06-30 - * @version 0.1 - * @since 0.20 - * @ingroup calculator - */ - -#include "ExpressionEvaluator.h" - -#include -#include - -#include - -namespace { -namespace eval = cf::desktop::desktop_component::calculator_core::ExpressionEvaluator; - -/// Evaluates @p expr and expects it near @p expected; fails on any throw. -void expectEq(const QString& expr, double expected, double tol = 1e-3) { - double result = 0; - try { - result = eval::evalute_expression(expr); - } catch (const std::exception& e) { - FAIL() << expr.toStdString() << " threw: " << e.what(); - } - EXPECT_NEAR(result, expected, tol) << expr.toStdString(); -} -} // namespace - -TEST(CalculatorParser, BasicArithmetic) { - expectEq("1 + 2", 3); - expectEq("5 - 3", 2); - expectEq("2 * 4", 8); - expectEq("8 / 2", 4); -} - -TEST(CalculatorParser, Precedence) { - expectEq("2 + 3 * 4", 14); - expectEq("(2 + 3) * 4", 20); - expectEq("10 - 2 - 3", 5); -} - -TEST(CalculatorParser, UnaryNegation) { - expectEq("-5", -5); - expectEq("-(2 + 3)", -5); - expectEq("4 + -2", 2); -} - -TEST(CalculatorParser, Functions) { - expectEq("sin(3.1415926 / 2)", 1.0); - expectEq("cos(0)", 1.0); - expectEq("sqrt(16)", 4.0); - expectEq("log(1)", 0.0); -} - -TEST(CalculatorParser, NestedExpressions) { - expectEq("((2+3)*(4+1))", 25.0); - expectEq("sqrt(4 + sqrt(16))", 2.828); -} - -TEST(CalculatorParser, BoundaryValues) { - expectEq("0", 0.0); - expectEq("0.00001 + 1", 1.00001); - expectEq("999999 + 1", 1000000.0); -} - -TEST(CalculatorParser, DeepNesting) { - expectEq("(((((((1+2)))))))", 3); - expectEq("(((((3+5)*2)-4)/2)+1)", 7); -} - -TEST(CalculatorParser, FunctionNesting) { - expectEq("sqrt(sin(3.1415926/2) + cos(0))", std::sqrt(1.0 + 1.0)); - expectEq("sqrt(sqrt(16))", 2.0); -} - -TEST(CalculatorParser, UnaryWithFunction) { - expectEq("-sin(3.1415926)", -std::sin(3.1415926)); - expectEq("-sqrt(9)", -3.0); - expectEq("sqrt(-3 * -3)", 3.0); -} - -TEST(CalculatorParser, MultipleNegation) { - expectEq("--2", 2.0); - expectEq("---2", -2.0); - expectEq("4 + --1", 5.0); -} - -TEST(CalculatorParser, LargeAndSmallNumbers) { - expectEq("1.0000000001 + 1", 2.0000000001); -} - -TEST(CalculatorParser, DecimalTolerance) { - expectEq(".5 + .5", 1.0); - expectEq("5.", 5.0); - expectEq("0.1 + 0.2", 0.3); -} - -TEST(CalculatorParser, MultiDivision) { - expectEq("100 / 2 / 5", 10.0); - expectEq("8 / 2 * (2 + 2)", 16.0); -} - -TEST(CalculatorParser, TrigIdentity) { - expectEq("sin(0)^2 + cos(0)^2", 1.0); - expectEq("log(exp(3))", 3.0); -} - -TEST(CalculatorParser, ThrowsOnMalformed) { - EXPECT_ANY_THROW(eval::evalute_expression("2 +")); - EXPECT_ANY_THROW(eval::evalute_expression("((3+2)")); - EXPECT_ANY_THROW(eval::evalute_expression("abc(3)")); - EXPECT_ANY_THROW(eval::evalute_expression("2 + * 3")); - EXPECT_ANY_THROW(eval::evalute_expression("3 / 0")); - EXPECT_ANY_THROW(eval::evalute_expression("10 / (5 - 5)")); - EXPECT_ANY_THROW(eval::evalute_expression("sqrt(-1)")); - EXPECT_ANY_THROW(eval::evalute_expression("sin")); - EXPECT_ANY_THROW(eval::evalute_expression("sin()")); - EXPECT_ANY_THROW(eval::evalute_expression("()")); - EXPECT_ANY_THROW(eval::evalute_expression("..2 + 1")); - EXPECT_ANY_THROW(eval::evalute_expression("1 + 2 3")); -}