Git commit a646c4c1a85859c2aaf342bff7eaeeaa8891de0c by Benson Muite, on behalf 
of Omar Tamer.
Committed on 20/09/2026 at 14:29.
Pushed by kbkmde into branch 'master'.

Feature: Add named-color support to color commands

M  +32   -0    doc/glossary.docbook
M  +4    -2    doc/programming-reference.docbook
M  +2    -0    src/CMakeLists.txt
A  +68   -0    src/color.cpp  *
A  +22   -0    src/color.h  *
M  +14   -0    src/highlighter.cpp
M  +1    -0    src/highlighter.h
M  +1    -0    src/inspector.cpp
M  +54   -7    src/interpreter/executer.cpp
M  +2    -0    src/interpreter/executer.h
M  +2    -1    src/interpreter/token.h

The files marked with a * at the end have a non valid license. Please read: 
https://community.kde.org/Policies/Licensing_Policy and use the headers which 
are listed at that page.


https://invent.kde.org/education/kturtle/-/commit/a646c4c1a85859c2aaf342bff7eaeeaa8891de0c

diff --git a/doc/glossary.docbook b/doc/glossary.docbook
index c6a1ed6..301bd91 100644
--- a/doc/glossary.docbook
+++ b/doc/glossary.docbook
@@ -80,6 +80,11 @@ penup
 <entry>Not much to say about (text) strings either, except that they always 
start and end with the double quotes (").</entry>
 </row>
 <row>
+<entry>named-colors</entry>
+<entry>depends on color</entry>
+<entry><glossterm linkend="named-colors">Named-colors</glossterm> will have a 
background color identical to them.</entry>
+</row>
+<row>
 <entry>numbers</entry>
 <entry>dark red</entry>
 <entry>Numbers, well not much to say about them.</entry>
@@ -152,6 +157,33 @@ penup
 <para>Two commands need an RGB combination as input: these commands are 
<userinput>canvascolor</userinput> and 
<userinput>pencolor</userinput>.</para></glossdef>
 </glossentry>
 
+<glossentry id="named-colors">
+<glossterm>Named-colors (color strings)</glossterm>
+<glossdef><para>In addition to RGB combinations, you can use named-colors. 
Kturtle supports a group of named-colors, which are passed as strings to the 
command requiring a color.
+<table frame="none">
+<title>Named-colors currently supported</title>
+<tgroup cols="1" rowsep="1">
+<tbody>
+<row><entry><userinput>"white"</userinput></entry></row>
+<row><entry><userinput>"black"</userinput></entry></row>
+<row><entry><userinput>"gray"</userinput></entry></row>
+<row><entry><userinput>"grey"</userinput></entry></row>
+<row><entry><userinput>"red"</userinput></entry></row>
+<row><entry><userinput>"brown"</userinput></entry></row>
+<row><entry><userinput>"orange"</userinput></entry></row>
+<row><entry><userinput>"yellow"</userinput></entry></row>
+<row><entry><userinput>"gold"</userinput></entry></row>
+<row><entry><userinput>"green"</userinput></entry></row>
+<row><entry><userinput>"blue"</userinput></entry></row>
+<row><entry><userinput>"purple"</userinput></entry></row>
+<row><entry><userinput>"pink"</userinput></entry></row>
+</tbody>
+</tgroup>
+</table>
+</para>
+<para>Two commands can use a named-color as input: these commands are 
<userinput>canvascolor</userinput> and 
<userinput>pencolor</userinput>.</para></glossdef>
+</glossentry>
+
 <glossentry id="sprite">
 <glossterm>sprite</glossterm>
 <glossdef><para>A sprite is a small picture that can be moved around the 
screen. Our beloved turtle, for instance, is a sprite.
diff --git a/doc/programming-reference.docbook 
b/doc/programming-reference.docbook
index 428112d..f23a094 100644
--- a/doc/programming-reference.docbook
+++ b/doc/programming-reference.docbook
@@ -363,7 +363,8 @@ Please note that <userinput>$A</userinput> and 
<userinput>$B</userinput> have to
     <varlistentry> 
       <term>pencolor (pc)<indexterm><primary>pencolor 
(pc)</primary></indexterm></term>
       <listitem><para><screen>pencolor R,G,B</screen>
-      <userinput>pencolor</userinput> sets the color of the pen. 
<userinput>pencolor</userinput> takes an <glossterm linkend="rgb">RGB 
combination</glossterm> as input. <userinput>pencolor</userinput> can be 
abbreviated to <userinput>pc</userinput>.</para></listitem>
+      <screen>pencolor "color"</screen>
+      <userinput>pencolor</userinput> sets the color of the pen. 
<userinput>pencolor</userinput> takes an <glossterm linkend="rgb">RGB 
combination</glossterm> or a <glossterm 
linkend="named-colors">named-color</glossterm> as input. 
<userinput>pencolor</userinput> can be abbreviated to 
<userinput>pc</userinput>.</para></listitem>
     </varlistentry>
   </variablelist>
 </sect2>
@@ -384,7 +385,8 @@ Please note that <userinput>$A</userinput> and 
<userinput>$B</userinput> have to
     <varlistentry> 
       <term>canvascolor (cc)<indexterm><primary>canvascolor 
(cc)</primary></indexterm></term>
       <listitem><para><screen>canvascolor R,G,B</screen>
-      <userinput>canvascolor</userinput> set the color of the canvas. 
<userinput>canvascolor</userinput> takes an <glossterm linkend="rgb">RGB 
combination</glossterm> as input. <userinput>canvascolor</userinput> can be 
abbreviated to <userinput>cc</userinput>.</para></listitem>
+      <screen>canvascolor "color"</screen>
+      <userinput>canvascolor</userinput> set the color of the canvas. 
<userinput>canvascolor</userinput> takes an <glossterm linkend="rgb">RGB 
combination</glossterm> or <glossterm 
linkend="named-colors">named-color</glossterm> as input. 
<userinput>canvascolor</userinput> can be abbreviated to 
<userinput>cc</userinput>.</para></listitem>
     </varlistentry>
   </variablelist>
 </sect2>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 795a759..7096201 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,6 +14,8 @@ target_sources(kturtle PRIVATE
      kturtle.qrc
     canvas.cpp
     canvas.h
+    color.h
+    color.cpp
     colorpicker.cpp
     colorpicker.h
     console.cpp
diff --git a/src/color.cpp b/src/color.cpp
new file mode 100644
index 0000000..8d379a3
--- /dev/null
+++ b/src/color.cpp
@@ -0,0 +1,68 @@
+#include "color.h"
+
+QColor Color::colorString2RGB(QString colorString)
+{
+    // default to white
+    QColor color = QColorConstants::White;
+    if (isValidColorString(colorString)) {
+        return colorMap[colorString];
+    }
+    return color;
+}
+
+bool Color::isValidColorString(QString colorString)
+{
+    static bool colorMapInitialized = false;
+    if (!colorMapInitialized) {
+        colorMapInitialized = true;
+        initColorMap();
+    }
+
+    if (colorMap.contains(colorString)) {
+        return true;
+    }
+    return false;
+}
+
+QColor Color::token2RGB(Token *token, ErrorList *errorList)
+{
+    QString colorString = token->look();
+    colorString.removeFirst(); // removing quotes
+    colorString.removeLast();
+    QColor color = QColorConstants::White;
+    if (isValidColorString(colorString)) {
+        return colorMap[colorString];
+    } else {
+        errorList->addError(i18n("%1 is not a valid color.", colorString), 
*token, 20000 + Token::Color * 100 + 90);
+    }
+    return color;
+}
+
+void Color::initColorMap()
+{
+    colorMap[i18n("white")] = QColorConstants::Svg::white;
+
+    colorMap[i18n("black")] = QColorConstants::Svg::black;
+
+    colorMap[i18n("grey")] = QColorConstants::Svg::gray;
+
+    colorMap[i18n("gray")] = QColorConstants::Svg::gray;
+
+    colorMap[i18n("red")] = QColorConstants::Svg::red;
+
+    colorMap[i18n("brown")] = QColorConstants::Svg::brown;
+
+    colorMap[i18n("orange")] = QColorConstants::Svg::orange;
+
+    colorMap[i18n("yellow")] = QColorConstants::Svg::yellow;
+
+    colorMap[i18n("gold")] = QColorConstants::Svg::gold;
+
+    colorMap[i18n("green")] = QColorConstants::Svg::green;
+
+    colorMap[i18n("blue")] = QColorConstants::Svg::blue;
+
+    colorMap[i18n("purple")] = QColorConstants::Svg::purple;
+
+    colorMap[i18n("pink")] = QColorConstants::Svg::pink;
+}
diff --git a/src/color.h b/src/color.h
new file mode 100644
index 0000000..d39c87d
--- /dev/null
+++ b/src/color.h
@@ -0,0 +1,22 @@
+#ifndef _COLOR_H_
+#define _COLOR_H_
+
+#include <KLocalizedString>
+#include <QColor>
+#include <QMap>
+
+#include "interpreter/errormsg.h"
+#include "interpreter/token.h"
+
+namespace Color
+{
+
+static QMap<QString, QColor> colorMap{};
+
+void initColorMap();
+bool isValidColorString(QString colorString);
+QColor colorString2RGB(QString colorString);
+QColor token2RGB(Token *token, ErrorList *errorList);
+}
+
+#endif
diff --git a/src/highlighter.cpp b/src/highlighter.cpp
index 6d66436..be7571c 100644
--- a/src/highlighter.cpp
+++ b/src/highlighter.cpp
@@ -5,6 +5,9 @@
 */
 
 #include "highlighter.h"
+#include "color.h"
+
+#include <KLocalizedString>
 
 Highlighter::Highlighter(QTextDocument *parent)
     : QSyntaxHighlighter(parent)
@@ -80,6 +83,10 @@ Token *Highlighter::checkOrApplyHighlighting(const QString 
&text, int cursorInde
 
 QTextCharFormat *Highlighter::tokenToFormat(Token *token)
 {
+    QString look = token->look();
+    look.removeFirst();
+    look.removeLast();
+
     switch (token->category()) {
     case Token::VariableCategory:
         return &variableFormat;
@@ -90,6 +97,13 @@ QTextCharFormat *Highlighter::tokenToFormat(Token *token)
     case Token::CommentCategory:
         return &commentFormat;
     case Token::StringCategory:
+        // qDebug() << look;
+        if (Color::isValidColorString(look)) {
+            QColor color = Color::colorString2RGB(look);
+            colorStringFormat.setBackground(color);
+            colorStringFormat.setForeground(color.lightness() >= 100 ? 
Qt::black : Qt::white);
+            return &colorStringFormat;
+        }
         return &stringFormat;
     case Token::ScopeCategory:
         return &scopeFormat;
diff --git a/src/highlighter.h b/src/highlighter.h
index 5b1bc0e..c210aec 100644
--- a/src/highlighter.h
+++ b/src/highlighter.h
@@ -47,6 +47,7 @@ private:
     QTextCharFormat trueFalseFormat;
     QTextCharFormat commentFormat;
     QTextCharFormat stringFormat;
+    QTextCharFormat colorStringFormat;
     QTextCharFormat numberFormat;
     QTextCharFormat scopeFormat;
     QTextCharFormat controllerCommandFormat;
diff --git a/src/inspector.cpp b/src/inspector.cpp
index 5b68281..9c453af 100644
--- a/src/inspector.cpp
+++ b/src/inspector.cpp
@@ -239,6 +239,7 @@ QTreeWidgetItem *Inspector::walkTree(TreeNode *node)
     QTextCharFormat *format = highlighter->tokenToFormat(node->token());
     if (format) {
         result->setForeground(0, format->foreground());
+        result->setBackground(0, format->background());
         QFont font(QFontDatabase::systemFont(QFontDatabase::FixedFont));
         font.setBold(format->font().bold());
         result->setFont(0, font);
diff --git a/src/interpreter/executer.cpp b/src/interpreter/executer.cpp
index 51154a2..6db89da 100644
--- a/src/interpreter/executer.cpp
+++ b/src/interpreter/executer.cpp
@@ -8,10 +8,12 @@
 // every aspect of it is slightly changed by Cies Breijs.
 
 #include "executer.h"
+#include "../color.h"
 
 #include <errno.h>
 #include <math.h>
 
+#include <QColor>
 #include <QDebug>
 #include <QRandomGenerator>
 #include <QTimer> // for wait
@@ -529,7 +531,7 @@ void Executer::executeVariable(TreeNode *node)
 void Executer::executeFunctionCall(TreeNode *node)
 {
     // //qDebug() << "called";
-    if (node->parent()->token()->type() == Token::Learn) { // in case we're 
defining a function
+    if (node->parent()->token()->type() == Token::Learn) {
         currentNode = node->parent();
         executeCurrent = true;
         return;
@@ -1098,21 +1100,66 @@ void Executer::executePenDown(TreeNode *node)
         return;
     Q_EMIT penDown();
 }
+
+/* Color Functions */
+
 void Executer::executePenColor(TreeNode *node)
 {
-    // //qDebug() << "called";
-    if (!checkParameterQuantity(node, 3, 20000 + Token::PenColor * 100 + 90) 
|| !checkParameterType(node, Value::Number, 20000 + Token::PenColor * 100 + 91))
+    uint nodeSize = node->childCount();
+    bool hasRgbValues = false;
+    bool hasColorString = false;
+
+    if (nodeSize == 3) {
+        hasRgbValues =
+            checkParameterQuantity(node, 3, 20000 + Token::PenColor * 100 + 
90) && checkParameterType(node, Value::Number, 20000 + Token::PenColor * 100 + 
90);
+    } else if (nodeSize == 1) {
+        hasColorString =
+            checkParameterQuantity(node, 1, 20000 + Token::PenColor * 100 + 
90) && checkParameterType(node, Value::String, 20000 + Token::PenColor * 100 + 
90);
+    }
+
+    if (!hasColorString && !hasRgbValues) {
+        addError(i18n("The command %1 takes either a string or %2 numbers", 
node->token()->look(), 3), *node->token(), 20000 + Token::PenColor * 100 + 90);
         return;
-    Q_EMIT penColor(node->child(0)->value()->number(), 
node->child(1)->value()->number(), node->child(2)->value()->number());
+    }
+
+    // //qDebug() << "called";
+    if (hasRgbValues) {
+        Q_EMIT penColor(node->child(0)->value()->number(), 
node->child(1)->value()->number(), node->child(2)->value()->number());
+    } else if (hasColorString) {
+        QColor color = Color::token2RGB(node->child(0)->token(), errorList);
+        Q_EMIT penColor(color.red(), color.green(), color.blue());
+    }
 }
 void Executer::executeCanvasColor(TreeNode *node)
 {
+    uint nodeSize = node->childCount();
+    bool hasRgbValues = false;
+    bool hasColorString = false;
+
+    if (nodeSize == 3) {
+        hasRgbValues = checkParameterQuantity(node, 3, 20000 + 
Token::CanvasColor * 100 + 90)
+            && checkParameterType(node, Value::Number, 20000 + 
Token::CanvasColor * 100 + 90);
+    } else if (nodeSize == 1) {
+        hasColorString = checkParameterQuantity(node, 1, 20000 + 
Token::CanvasColor * 100 + 90)
+            && checkParameterType(node, Value::String, 20000 + 
Token::CanvasColor * 100 + 90);
+    }
+
     // //qDebug() << "called";
-    if (!checkParameterQuantity(node, 3, 20000 + Token::CanvasColor * 100 + 90)
-        || !checkParameterType(node, Value::Number, 20000 + Token::CanvasColor 
* 100 + 91))
+    if (!hasColorString && !hasRgbValues) {
+        addError(i18n("The command %1 takes either a string or %2 numbers", 
node->token()->look(), 3), *node->token(), 20000 + Token::PenColor * 100 + 90);
         return;
-    Q_EMIT canvasColor(node->child(0)->value()->number(), 
node->child(1)->value()->number(), node->child(2)->value()->number());
+    }
+
+    if (hasRgbValues) {
+        Q_EMIT canvasColor(node->child(0)->value()->number(), 
node->child(1)->value()->number(), node->child(2)->value()->number());
+    } else if (hasColorString) {
+        QColor color = Color::token2RGB(node->child(0)->token(), errorList);
+        Q_EMIT canvasColor(color.red(), color.green(), color.blue());
+    }
 }
+
+/* End Color Functions */
+
 void Executer::executeCanvasSize(TreeNode *node)
 {
     // //qDebug() << "called";
diff --git a/src/interpreter/executer.h b/src/interpreter/executer.h
index f1b1878..087f31d 100644
--- a/src/interpreter/executer.h
+++ b/src/interpreter/executer.h
@@ -7,6 +7,7 @@
 #ifndef _EXECUTER_H_
 #define _EXECUTER_H_
 
+#include <QColor>
 #include <QHash>
 #include <QObject>
 #include <QStack>
@@ -239,6 +240,7 @@ private:
 
     TreeNode *getParentOfTokenTypes(TreeNode *child, QList<int> *types);
     inline void printExe();
+    QColor colorString2RGB(QString colorString);
 
 Q_SIGNALS:
     void currentlyExecuting(TreeNode *node);
diff --git a/src/interpreter/token.h b/src/interpreter/token.h
index b9dff9d..4b05ea4 100644
--- a/src/interpreter/token.h
+++ b/src/interpreter/token.h
@@ -132,7 +132,8 @@ public:
         Sqrt,
         Round,
         GetDirection,
-        Mod
+        Mod,
+        Color
 
         // END GENERATED token_type_h CODE
     };

Reply via email to