Skip to content

Latest commit

 

History

History
785 lines (739 loc) · 44.6 KB

File metadata and controls

785 lines (739 loc) · 44.6 KB

مـنصة_ويب (WebPlatform)

[English]

[رجوع]

أمثلة

التعامل مع المرسم

اشمل "مـحا"؛
مـحا.اشمل_حزمة("Alusus/WebPlatform@0.9"، "مـنصة_ويب.أسس")؛
مـحا.اشمل_حزمة("Alusus/Http@0.3"، "بـننف.أسس")؛
مـحا.اشمل_حزمة("Alusus/Json@0.2"، "جـيسون.أسس")؛
مـحا.اشمل_حزمة("Alusus/Promises@0.1"، "مـؤجلات.أسس")؛
اشمل "مـتم/سندات"؛
اشمل "مـتم/مـصفوفة"؛
اشمل "مـتم/نـص"؛
اشمل "مـتم/طـرفية"؛
اشمل "مـتم/ريـاضيات"؛

استخدم مـتم؛
استخدم مـنصة_ويب؛

//=============================================================================
// الخادم

// نقوم بتعريف المسار الذي سنخزن عليه الموارد
@مسار_موارد عرف مسار_الموارد: "Assets/"؛

//=============================================================================
// واجهة المستخدم

عرف _خط_الكتابة_: "30px AlususMono"؛
عرف _لون_اصلي_: لـون("8e50ef")؛

// دالة لإنشاء ضبيطة الترويسة
دالة أنشئ_الترويسة():سـندنا[Widget]{
    // هنا نقوم بإنشاء مساحة مستطيلة لوضع مكونات الضبيطة ضمنها
    أرجع صـندوق({}).{
        // الأطرزة المستعملة لهذه الضبيطة
        الطراز.{
            العرض = مـسافة.مئوي(100)؛
            الطول = مـسافة.نقاط(40)؛
            الحشوة = مـسافة4.نقاط(5، 0)؛
            الخلفية = خـلفية(_لون_اصلي_)؛
            لون_الإطار = _لون_اصلي_؛
            سمك_الإطار = مـسافة4.نقاط(1.5)؛
            طراز_الإطار = طـراز_إطار._مستمر_؛
            الإظهار = إظـهار._مرن_؛
            النسق = نـسق._عمود_؛
        }؛
        // مكونات الضبيطة و التي نعتبرها فروع للضبيطة
        أضف_فروع({
            // هنا سيكون لدينا فقط نص يوضح أننا ننشأ مثال عن المرسم
            كـتابة(نـص("أمثلة منصة ويب الأسس - مرسم")).{ الطراز.{
                لون_الخط = لـون("fff")؛
                حجم_الخط = مـسافة.نقاط(21.0)؛
                العرض = مـسافة.مئوي(100)؛
                الطول = مـسافة.مئوي(100)؛
                الخلفية = خـلفية(_لون_اصلي_)؛
                الهامش = مـسافة4.نقاط(5، 15)؛
            } }
        })؛
    }؛
}

// صنف لنضع ضمنه معلومات الموقع ضمن الشاشة على شكل إحداثيات
صنف مـوقع {
    عرف س: صحيح؛
    عرف ص: صحيح؛
}

// منفذ بياني لموقع الويب خاصتنا
// هنا نحدد أولاً المسار و سيكون لدينا الجذر هنا
// و ثانياً نضع العنوان الخاص بهذا المنفذ البياني
@منفذ_مرئي["/"، "مثال منصة ويب - مرسم"]
دالة رئيسي {
    // سنقوم هنا بتحميل الموارد من السيرفر
    // في البداية نقوم بتعريف مورد من أجل كل نوع موارد نريده
    // مورد للصورة
    عرف صورة: مـورد_صورة؛
    // مورد للصوت
    عرف موسيقى: مـورد_صوت؛
    // مورد للصوت
    عرف صوت: مـورد_صوت؛
    // نقوم بتحميل الموارد بشكل لامتزامن
    انتظر(مـؤجلات.مـؤجلة[صحيح].الكل({
        // لتحميل الصورة يكفي استدعاء تابع التحميل مع مسار الصورة
        صورة.حمل("Assets/logo.svg"),
        // نقوم بتحميل الخط عن طريق اسم الخط و المسار الخاص به
        حمل_خط("AlususMono", "Assets/AlususMono.otf"),
        // أيضاً لتحميل الصوت يكفي استدعاء تابع التحميل مع مسار الملف الصوتي
        موسيقى.حمل("Assets/music.mp3"),
        صوت.حمل("Assets/sound.mp3")
    }).تجاهل_النتيجة())؛

    // نقوم بتعريف بعض المتغيرات التي سنحتاجها تالياً
    // متغير لتخزين الموقع
    عرف موقع: مـوقع؛
    موقع.س = 200؛
    موقع.ص = 150؛
    // متغير لتخزين الحركة
    عرف تغير: مـوقع؛
    تغير.س = تغير.ص = 0؛
    // متغير لتخزين فيما إذا تم الضغط على المؤشر أم لا
    عرف مضغوط: ثنائي = 0؛
    // متغير لتخزين فيما إذا قد تم قفل المؤشر أم لا
    عرف المؤشر_محتكر: ثنائي = 0؛
    // متغير لتحديد فيما إذا كنا في وضع ملء الشاشة أم لا
    عرف ملء_الشاشة: ثـنائي = 0؛
    // متغير لتخزين عدد المقابض
    عرف عدد_المقابض: صحيح = 0؛

    // بناء المشهد
    عرف مرسم: سـندنا[مـرسم]؛
    // نقوم بتطبيق بعض الأطرزة على النافذة
    // هنا مثلاً طبقنا أننا لا نريد حشو أو هوامش
    نـافذة.النموذج.الطراز.{
        الحشوة = مـسافة4.نقاط(0)؛
        الهامش = مـسافة4.نقاط(0)؛
    }؛
    // نقوم بتحديد المشهد الخاص بالنافذة
    نـافذة.النموذج.حدد_المشهد(صـندوق({}).{
        // بعض الأطرزة لتطبيقها على المساحة المربعة التي سنعرف ضمنها المرسم
        // و الأدوات الخاصة به
        الطراز.{
            المحاذاة = مـحاذاة._وسط_؛
            الإظهار = إظـهار._مرن_؛
            النسق = نـسق._عمود_؛
            الاتجاه = اتـجاه._من_اليمين_؛
        }؛
        أضف_فروع({
            // نقوم بإضافة الترويسة التي عرفنا دالة إضافتها سابقاً.
            أنشئ_الترويسة(),
            // مساحة مربعة أخرى لإضافة بعض الخيارات الخاصة بالصوت
            صـندوق({}).{
                الطراز.الإظهار = إظـهار._مرن_؛
                الطراز.النسق = نـسق._سطر_؛
                أضف_فروع({
                    // زر لتشغيل الموسيقى
                    الـزر(نـص("شغل الموسيقى")).{
                        الطراز.{
                            الطول = مـسافة.نقاط(30)؛
                            العرض = مـسافة.نقاط(120)؛
                            الخلفية = خـلفية(لـون(200, 200, 200))؛
                            حجم_الخط = مـسافة.نقاط(16.0)؛
                        }؛
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بتشغيل الموسيقى
                        // و ذلك عبر استدعاء تابع التشغيل الخاص بالمورد
                        // نلاحظ هنا أننا نمرر أننا نريد تشغيل بالموسيقى بشكل متكرر
                        عند_الضغط.اربط(مغلفة (موسيقى: كسند)&(ضبيطة: سند[ضـبيطة], حمولة: سند[صحيح]) {
                            موسيقى.شغل(1)؛
                        })؛
                    },
                    // زر لإيقاف تشغيل الموسيقى
                    الـزر(نـص("أوقف الموسيقى")).{
                        الطراز.{
                            الطول = مـسافة.نقاط(30)؛
                            العرض = مـسافة.نقاط(120)؛
                            الخلفية = خـلفية(لـون(200, 200, 200))؛
                            حجم_الخط = مـسافة.نقاط(16.0)؛
                        }؛
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بإيقاف الموسيقى
                        // و ذلك عبر استدعاء تابع الإيقاف الخاص بالمورد
                        عند_الضغط.اربط(مغلفة (موسيقى: كسند)&(ضبيطة: سند[ضـبيطة], حمولة: سند[صحيح]) {
                            موسيقى.أوقف()؛
                        })؛
                    },
                    // زر لتشغيل الصوت
                    الـزر(نـص("شغل الصوت")).{
                        الطراز.{
                            الطول = مـسافة.نقاط(30)؛
                            العرض = مـسافة.نقاط(120)؛
                            الخلفية = خـلفية(لـون(200, 200, 200))؛
                            حجم_الخط = مـسافة.نقاط(16.0)؛
                        }؛
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بتشغيل الصوت
                        // و ذلك عبر استدعاء تابع التشغيل الخاص بالمورد
                        // نلاحظ هنا أننا نشغل الصوت لمرة واحدة فقط بما أننا مررنا قيمة خطأ للتكرار
                        عند_الضغط.اربط(مغلفة (صوت: كسند)&(ضبيطة: سند[ضـبيطة], حمولة: سند[صحيح]) {
                            صوت.شغل(0)؛
                        })؛
                    },
                    // زر للقيام بتشغيل الصوت بشكل مستمر
                    الـزر(نـص("شغل الصوت بتكرار")).{
                        الطراز.{
                            الطول = مـسافة.نقاط(30)؛
                            العرض = مـسافة.نقاط(150)؛
                            الخلفية = خـلفية(لـون(200, 200, 200))؛
                            حجم_الخط = مـسافة.نقاط(16.0)؛
                        }؛
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بتشغيل الصوت
                        // و ذلك عبر استدعاء تابع التشغيل الخاص بالمورد
                        // نلاحظ هنا أننا نمرر أننا نريد تشغيل بالصوت بشكل متكرر
                        عند_الضغط.اربط(مغلفة (صوت: كسند)&(ضبيطة: سند[ضـبيطة], حمولة: سند[صحيح]) {
                            صوت.شغل(1)؛
                        })؛
                    },
                    // تابع ﻷيقاف الصوت
                    الـزر(نـص("أوقف الصوت")).{
                        الطراز.{
                            الطول = مـسافة.نقاط(30)؛
                            العرض = مـسافة.نقاط(120)؛
                            الخلفية = خـلفية(لـون(200, 200, 200))؛
                            حجم_الخط = مـسافة.نقاط(16.0)؛
                        }؛
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بإيقاف الصوت
                        // و ذلك عبر استدعاء تابع الإيقاف الخاص بالمورد
                        عند_الضغط.اربط(مغلفة (صوت: كسند)&(ضبيطة: سند[ضـبيطة], حمولة: سند[صحيح]) {
                            صوت.أوقف()؛
                        })؛
                    }
                })؛
            }
            // نقوم بتعريف الضبيطة الخاصة بالمرسم
            مـرسم().{
                مرسم = هذا؛
                // نقوم بتحديد الطول و العرض
                عرض_الصورة = مـسافة.بكسلات(1280)؛
                طول_الصورة = مـسافة.بكسلات(800)؛
                // نقوم بتطبيق بعض الأطرزة على المرسم
                الطراز.{
                    العرض = مـسافة.مئوي(100)؛
                    الطول = مـسافة.مئوي(100)؛
                    أقصى_عرض = مـسافة.بكسلات(1280)؛
                    أقصى_طول = مـسافة.بكسلات(800)؛
                    سمك_الإطار = مـسافة4.نقاط(2)؛
                    طراز_الإطار = طـراز_إطار._مستمر_؛
                    لون_الإطار = لـون(0, 0, 0)؛
                    الخلفية = خـلفية(لـون(220, 220, 220))؛
                }؛
                عرف أبعاد: أبـعاد(1، 1)؛
                // نقوم بربط إشارة تحريك المؤشر بمغلف بحيث يتم استدعاءه
                // عند تحريك المؤشر لتحديث قيم الموقع
                عند_تحريك_المؤشر.اربط(
                    مغلفة (
                        موقع: كسند، أبعاد: كسند، المؤشر_محتكر: كسند
                    )&(
                        ضبيطة: سند[ضـبيطة]، حمولة: سند[حـمولة_تحريك_المؤشر]
                    ) {
                        إذا المؤشر_محتكر {
                            موقع.س += حمولة.إزاحة_س؛
                            موقع.ص += حمولة.إزاحة_ص؛
                        } وإلا {
                            موقع.س = حمولة.موقع_س * 1280 ÷ أبعاد.عرض؛
                            موقع.ص = حمولة.موقع_ص * 800 ÷ أبعاد.طول؛
                        }
                    }
                )؛
                // نقوم بربط إشارة دخول المؤشر بمغلف بحيث يتم استدعاءه
                // عند دخول المؤشر و ذلك لتغيير لون الإطار إلى قيمته الأصلية
                عند_دخول_المؤشر.اربط(مغلفة (ضبيطة: سند[ضـبيطة]، حمولة: سند[صحيح]) {
                    هذا.الطراز.لون_الإطار = لـون(0، 0، 0)؛
                })؛
                // نقوم بربط إشارة خروج المؤشر بمغلف بحيث يتم استدعاءه
                // عند خروج المؤشر و ذلك لتغيير لون الإطار إلى الأحمر لتوضيح أننا خارج المرسم
                عند_خروج_المؤشر.اربط(مغلفة (ضبيطة: سند[ضـبيطة]، حمولة: سند[صحيح]) {
                    هذا.الطراز.لون_الإطار = لـون(255، 0، 0)؛
                })؛
                // نقوم بربط إشارة بدء الضفط على المؤشر بمغلف بحيث يتم استدعاءه
                // عند بدء الضفط على المؤشر و ذلك لتغيير قيمة المتغير الخاص بتحديد فيما إذا تم
                // الضغط على المؤشر إلى القيمة المناسبة و ذلك فقط إذا تم الضغط على الزر الأيسر
                عند_بدء_النقرة.اربط(مغلفة (مضغوط: كسند)&(ضبيطة: سند[ضـبيطة]، حمولة: سند[حـمولة_زر_المؤشر]) {
                    إذا حمولة.الزر == 0 مضغوط = 1؛
                })؛
                // نقوم بربط إشارة نهاية الضفط على المؤشر بمغلف بحيث يتم استدعاءه
                // عند نهاية الضفط على المؤشر و ذلك لتغيير قيمة المتغير الخاص بتحديد فيما إذا تم
                // الضغط على المؤشر إلى القيمة المناسبة و ذلك فقط إذا تم إيقاف الضغط على الزر الأيسر
                عند_انتهاء_النقرة.اربط(مغلفة (مضغوط: كسند)&(ضبيطة: سند[ضـبيطة]، حمولة: سند[حـمولة_زر_المؤشر]) {
                    إذا حمولة.الزر == 0 مضغوط = 0؛
                })؛
                // نقوم بربط إشارة تغيير الحجم بمغلف بحيث يتم استدعاءه
                // عند تغيير الحجم و ذلك لتغيير الأبعاد فيما يتناسب مع تغير الحجم
                عند_تغير_الأبعاد.اربط(مغلفة (أبعاد: كسند)&(ضبيطة: سند[ضـبيطة]، حمولة: سند[صحيح]) {
                    أبعاد = هذا.هات_الأبعاد()؛
                })؛
            }
        })؛
    })؛

    // سنقوم فيما يلي بتعريف ما يلزم من أجل استعمال المقبض مع المرسم
    نـافذة.النموذج.{
        // المفاتيح التي نريدها هي الاتجاهات فقط
        المفاتيح_المكبوتة = "ArrowUp,ArrowDown,ArrowLeft,ArrowRight"؛
        // نقوم بربط إشارة بدء الضغط على المؤشر بمؤجل يقوم عند استدعاءه
        // بتحديد التغير في الموقع بناء على الزر المضغوط
        عند_بدء_الكبسة.اربط(مغلفة (
            تغير: كسند، المؤشر_محتكر: كسند، ملء_الشاشة: كسند
        )&(
            نافذة: سند[نـافذة]، حمولة: سند[نـص]
        ) {
            إذا حمولة == "ArrowUp" تغير.ص = -5
            وإلا إذا حمولة == "ArrowDown" تغير.ص = 5
            وإلا إذا حمولة == "ArrowRight" تغير.س = 5
            وإلا إذا حمولة == "ArrowLeft" تغير.س = -5
            // كما يقوم هنا بالدخول إلى وضع ملء الشاشة أو الخروج منه
            وإلا إذا حمولة == "Enter" {
                إذا ملء_الشاشة ألغ_ملء_الشاشة()
                وإلا مرسم.التمس_ملء_الشاشة()؛
            }
            // أيضاً نقوم بطلب قفل المؤشر أو الخروج منه
            وإلا إذا حمولة == "Space" {
                إذا المؤشر_محتكر ألغ_احتكار_المؤشر()
                وإلا مرسم.التمس_احتكار_المؤشر()؛
            }
        })؛
        // عند توقف الضغط على المؤشر نقوم بتصفير قيم الحركة
        عند_انتهاء_الكبسة.اربط(مغلفة (تغير: كسند)&(نافذة: سند[نـافذة]، حمولة: سند[نـص]) {
            إذا حمولة == "ArrowUp" تغير.ص = 0
            وإلا إذا حمولة == "ArrowDown" تغير.ص = 0
            وإلا إذا حمولة == "ArrowRight" تغير.س = 0
            وإلا إذا حمولة == "ArrowLeft" تغير.س = 0؛
        })؛
        // نقوم بربط إشارة قفل المؤشر بمؤجل عند تنفيذه يتم تحديث قيمة المتغير الذي نحدد
        // ضمنه فيما إذا كنا في وضع غلق المؤشر أم لا
        عند_تغير_احتكار_المؤشر.اربط(مغلفة (المؤشر_محتكر: كسند)&(نافذة: سند[نـافذة]، حمولة: سند[ثـنائي]) {
            المؤشر_محتكر = حمولة؛
        })؛
        // نقوم بربط إشارة تغير وضع ملء الشاشة بمؤجل عند تنفيذه يتم تحديث قيمة المتغير
        // الذي نخزن ضمنه فيما إذا كنا في وضع ملء الشاشة أم لا
        // كما أننا نقوم بقفل المؤشر عند الدخول في وضع ملء الشاشة و الخروج منه عكس ذلك
        عند_تغير_ملء_الشاشة.اربط(مغلفة (ملء_الشاشة: كسند)&(نافذة: سند[نـافذة]، حمولة: سند[ثـنائي]) {
            ملء_الشاشة = حمولة؛
            إذا حمولة مرسم.التمس_احتكار_المؤشر()
            وإلا ألغ_احتكار_المؤشر()؛
        })؛
        // نقوم بربط إشارات ربط المقيض و فصله بمؤجل عند تنفيذ يتم تحديث
        // قيمة المتغير الذي يخزن عدد المقابض الحالية
        عند_ربط_مقبض.اربط(مغلفة (عدد_المقابض: كسند)&(نافذة: سند[نـافذة]، حمولة: سند[نـص]) {
            عدد_المقابض = هات_عدد_المقابض()؛
        })؛
        عند_فصل_مقبض.اربط(مغلفة (عدد_المقابض: كسند)&(نافذة: سند[نـافذة]، حمولة: سند[نـص]) {
            عدد_المقابض = هات_عدد_المقابض()؛
        })؛
    }؛

    // توليد صورة بشكل ديناميكي
    // مورد الصورة
    عرف صورة_ديناميكية: مـورد_صورة؛
    // المرسم الذي سنرسم عليه
    عرف مورد_مرسم: مـورد_مرسم(100, 100)؛
    مورد_مرسم.حدد_عرض_القلم(5)؛
    مورد_مرسم.حدد_نمط_القلم("#ff5555")؛
    // نقوم برسم خطين على المرسم
    مورد_مرسم.ارسم_خطا(0, 0, 100, 100)؛
    مورد_مرسم.ارسم_خطا(100, 0, 0, 100)؛
    // نقوم برسم مضلع على المرسم
    عرف نقاط: مـصفوفة[صحيح]({ 25, 25, 75, 25, 75, 75, 25, 75 })؛
    مورد_مرسم.ارسم_مضلعا(4, نقاط.صوان, 0)؛
    // نقوم بتحميل الصورة من المرسم
    صورة_ديناميكية.هيئ_من_مرسم(مورد_مرسم)؛

    // حلقة التحريك
    عرف س: صحيح = -80؛
    // نبدأ مؤقت يقوم بتنفيذ ما نريد كل 8 ثواني
    ابدأ_المؤقت_المتكرر(8000, مغلفة (
        س: كسند, صورة: كسند, صورة_ديناميكية: كسند، موقع: كسند، تغير: كسند، مضغوط: كسند، عدد_المقابض: كسند
    ) & (جسون: جـيسون) {
        // في كل مرة نقوم بمحو الرسومات للبدء من جديد
        مرسم.فرغ()؛
        // نرسم خط
        مرسم.ارسم_خطا(0,0, 100,100)؛
        // نرسم دائرة
        مرسم.ارسم_دائرة(100,180, 90)؛
        // نقوم برسم مضلع
        عرف نقاط: مـصفوفة [صحيح]({ 50, 50, 100, 50, 100, 100, 50, 100, 25, 75 })؛
        مرسم.حدد_نمط_الملء("black", "blue", 0, 0, 100, 100)؛
        مرسم.ارسم_مضلعا(5, نقاط.صوان, 1)؛
        // نرسم نص
        عرف _كتابة_: "الأسس"؛
        مرسم.ارسم_كتابة(_كتابة_, _خط_الكتابة_, 550, 150)؛

        عرف حجم: صحيح = ريـاضيات.جا(3.14 * س / 320.0) * 40 + 100؛
        عرف ص1: صحيح = ريـاضيات.جا(3.14 * س / 320.0) * 100 + 400 - حجم / 2؛
        عرف ص2: صحيح = ريـاضيات.جتا(3.14 * س / 320.0) * 100 + 400 - حجم / 2؛
        عرف شفافية: عائم = ريـاضيات.جا(3.14 * س / 80.0) / 4 + 0.75؛
        مرسم.ارسم_صورة(صورة, س, ص1, حجم, حجم, شفافية)؛
        مرسم.ارسم_صورة(صورة_ديناميكية, س, ص2, حجم, حجم, شفافية)؛

        // في كل مرة في حال كان لدينا مقابض نقوم بجلب الحركة التي تمت بواسطتها
        // و نقوم بالتحريك كما يلزم
        إذا عدد_المقابض > 0 {
            تغير.س = هات_قيمة_محور_المقبض(0، 0) * 5؛
            تغير.ص = هات_قيمة_محور_المقبض(0، 1) * 5؛
            مضغوط = هات_قيمة_زر_المقبض(0، 0) > 0.5؛
        }

        إذا مضغوط مرسم.ارسم_صورة(صورة, موقع.س, موقع.ص, حجم, حجم, شفافية)
        وإلا مرسم.ارسم_صورة(صورة_ديناميكية, موقع.س, موقع.ص, حجم, حجم, شفافية)؛
        إذا ++س >= 1280 س = -80؛
        موقع.س += تغير.س؛
        موقع.ص += تغير.ص؛
    })؛

    نفذ_حلقة_معالجة_الأحداث()؛
}


طـرفية.اطبع("تشغيل الخادم على المنفذ 8010...\nURL: http://localhost:8010/\n")؛
// نبدأ تشغيل السيرفر على المنفذ 8010
ابن_وشغل_الخادم(مـصفوفة[مـؤشر_محارف]({ "listening_ports"، "8010"، "static_file_max_age"، "0" }))؛
import "Srl/Math";
import "Apm";
Apm.importPackage("Alusus/WebPlatform@0.9");
use Srl;
use WebPlatform;

//==============================================================================
// Backend

// نقوم بتعريف المسار الذي سنخزن عليه الموارد
@assetsRoute def assetsRoute: "Assets/";

//==============================================================================
// Frontend

def PRIMARY_COLOR: Color("8e50ef");

// دالة لإنشاء ضبيطة الترويسة
func createHeader (): SrdRef[Widget] {
    // هنا نقوم بإنشاء مساحة مستطيلة لوضع مكونات الضبيطة ضمنها
    return Box({}).{
        // الأطرزة المستعملة لهذه الضبيطة
        style.{
            width = Length.percent(100);
            height = Length.pt(40);
            padding = Length4.pt(5, 0);
            background = Background(PRIMARY_COLOR);
            borderColor = PRIMARY_COLOR;
            borderWidth = Length4.pt(1.5);
            borderStyle = BorderStyle.SOLID;
            display = Display.FLEX;
            layout = Layout.COLUMN;
        };
        // مكونات الضبيطة و التي نعتبرها فروع للضبيطة
        addChildren({
            // هنا سيكون لدينا فقط نص يوضح أننا ننشأ مثال عن المرسم
            Text(String("Alusus Web Platform Examples - Canvas")).{ style.{
                fontColor = Color("fff");
                fontSize = Length.pt(21.0);
                width = Length.percent(100);
                height = Length.percent(100);
                background = Background(PRIMARY_COLOR);
                margin = Length4.pt(5, 15);
            } },
        });
    }~cast[SrdRef[Widget]];
}

// صنف لنضع ضمنه معلومات الموقع ضمن الشاشة على شكل إحداثيات
class Position {
    def x: Int;
    def y: Int;
}

// منفذ بياني لموقع الويب خاصتنا
// هنا نحدد أولاً المسار و سيكون لدينا الجذر هنا
// و ثانياً نضع العنوان الخاص بهذا المنفذ البياني
@uiEndpoint["/", "WebPlatform Example - Canvas"]
func main {
    // سنقوم هنا بتحميل الموارد من السيرفر
    // في البداية نقوم بتعريف مورد من أجل كل نوع موارد نريده
    def img: ImageResource;  // مورد للصورة
    def music: AudioResource;  // مورد للصوت
    def sound: AudioResource;  // مورد للصوت
    // نقوم بتحميل الموارد بشكل لامتزامن
    await(Promises.Promise[Int].all({
        // لتحميل الصورة يكفي استدعاء تابع التحميل مع مسار الصورة
        img.load("Assets/logo.svg"),
        // نقوم بتحميل الخط عن طريق اسم الخط و المسار الخاص به
        loadFont("AlususMono", "Assets/AlususMono.otf"),
        // أيضاً لتحميل الصوت يكفي استدعاء تابع التحميل مع مسار الملف الصوتي
        music.load("Assets/music.mp3"),
        sound.load("Assets/sound.mp3")
    }).ignoreResult());

    // نقوم بتعريف بعض المتغيرات التي سنحتاجها تالياً
    // متغير لتخزين الموقع
    def pos: Position;
    pos.x = 200;
    pos.y = 150;
    // متغير لتخزين الحركة
    def delta: Position;
    delta.x = delta.y = 0;
    // متغير لتخزين فيما إذا تم الضغط على المؤشر أم لا
    def pressed: Bool = 0;
    // متغير لتخزين فيما إذا قد تم قفل المؤشر أم لا
    def pointerLocked: Bool = false;
    // متغير لتحديد فيما إذا كنا في وضع ملء الشاشة أم لا
    def fullscreen: Bool = false;
    // متغير لتخزين عدد المقابض
    def gamepadsCount: Int = 0;

    // بناء المشهد
    def canvas: SrdRef[Canvas];
    // نقوم بتطبيق بعض الأطرزة على النافذة
    // هنا مثلاً طبقنا أننا لا نريد حشو أو هوامش
    Window.instance.style.{
        padding = Length4.pt(0);
        margin = Length4.pt(0);
    };
    // نقوم بتحديد المشهد الخاص بالنافذة
    Window.instance.setView(Box({}).{
        // بعض الأطرزة لتطبيقها على المساحة المربعة التي سنعرف ضمنها المرسم
        // و الأدوات الخاصة به
        style.{
            align = Align.CENTER;
            display = Display.FLEX;
            layout = Layout.COLUMN;
        };
        addChildren({
            // نقوم بإضافة الترويسة التي عرفنا دالة إضافتها سابقاً.
            createHeader(),
            // مساحة مربعة أخرى لإضافة بعض الخيارات الخاصة بالصوت
            Box({}).{
                style.display = Display.FLEX;
                style.layout = Layout.ROW;
                addChildren({
                    // زر لتشغيل الموسيقى
                    Button(String("Play Music")).{
                        style.{
                            height = Length.pt(30);
                            width = Length.pt(120);
                            background = Background(Color(200, 200, 200));
                            fontSize = Length.pt(16.0);
                        };
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بتشغيل الموسيقى
                        // و ذلك عبر استدعاء تابع التشغيل الخاص بالمورد
                        // نلاحظ هنا أننا نمرر أننا نريد تشغيل بالموسيقى بشكل متكرر
                        onClick.connect(closure (music: by_ref)&(widget: ref[Widget], payload: ref[Int]) {
                            music.play(true);
                        });
                    },
                    // زر لإيقاف تشغيل الموسيقى
                    Button(String("Stop Music")).{
                        style.{
                            height = Length.pt(30);
                            width = Length.pt(120);
                            background = Background(Color(200, 200, 200));
                            fontSize = Length.pt(16.0);
                        };
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بإيقاف الموسيقى
                        // و ذلك عبر استدعاء تابع الإيقاف الخاص بالمورد
                        onClick.connect(closure (music: by_ref)&(widget: ref[Widget], payload: ref[Int]) {
                            music.stop();
                        });
                    },
                    // زر لتشغيل الصوت
                    Button(String("Play Sound")).{
                        style.{
                            height = Length.pt(30);
                            width = Length.pt(120);
                            background = Background(Color(200, 200, 200));
                            fontSize = Length.pt(16.0);
                        };
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بتشغيل الصوت
                        // و ذلك عبر استدعاء تابع التشغيل الخاص بالمورد
                        // نلاحظ هنا أننا نشغل الصوت لمرة واحدة فقط بما أننا مررنا قيمة خطأ للتكرار
                        onClick.connect(closure (sound: by_ref)&(widget: ref[Widget], payload: ref[Int]) {
                            sound.play(false);
                        });
                    },
                    // زر للقيام بتشغيل الصوت بشكل مستمر
                    Button(String("Play Repeating Sound")).{
                        style.{
                            height = Length.pt(30);
                            width = Length.pt(180);
                            background = Background(Color(200, 200, 200));
                            fontSize = Length.pt(16.0);
                        };
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بتشغيل الصوت
                        // و ذلك عبر استدعاء تابع التشغيل الخاص بالمورد
                        // نلاحظ هنا أننا نمرر أننا نريد تشغيل بالصوت بشكل متكرر
                        onClick.connect(closure (sound: by_ref)&(widget: ref[Widget], payload: ref[Int]) {
                            sound.play(true);
                        });
                    },
                    // تابع ﻷيقاف الصوت
                    Button(String("Stop Sound")).{
                        style.{
                            height = Length.pt(30);
                            width = Length.pt(120);
                            background = Background(Color(200, 200, 200));
                            fontSize = Length.pt(16.0);
                        };
                        // نقوم هنا بربط إشارة الضفط على هذا الزر بتابع يقوم بإيقاف الصوت
                        // و ذلك عبر استدعاء تابع الإيقاف الخاص بالمورد
                        onClick.connect(closure (sound: by_ref)&(widget: ref[Widget], payload: ref[Int]) {
                            sound.stop();
                        });
                    }
                });
            }
            // نقوم بتعريف الضبيطة الخاصة بالمرسم
            Canvas().{
                canvas = this;
                // نقوم بتحديد الطول و العرض
                bitmapWidth = Length.px(1280);
                bitmapHeight = Length.px(800);
                // نقوم بتطبيق بعض الأطرزة على المرسم
                style.{
                    width = Length.percent(100);
                    height = Length.percent(100);
                    maxWidth = Length.px(1280);
                    maxHeight = Length.px(800);
                    borderWidth = Length4.pt(2);
                    borderStyle = BorderStyle.SOLID;
                    borderColor = Color(0, 0, 0);
                    background = Background(Color(220, 220, 220));
                };
                def dim: Dimensions(1, 1);
                // نقوم بربط إشارة تحريك المؤشر بمغلف بحيث يتم استدعاءه
                // عند تحريك المؤشر لتحديث قيم الموقع
                onMouseMove.connect(closure (
                    pos: by_ref, dim: by_ref, pointerLocked: by_ref
                )&(
                    widget: ref[Widget], payload: ref[MouseMovePayload]
                ) {
                    if pointerLocked {
                        pos.x += payload.deltaX;
                        pos.y += payload.deltaY;
                    } else {
                        pos.x = payload.posX * 1280 / dim.width;
                        pos.y = payload.posY * 800 / dim.height;
                    }
                });
                // نقوم بربط إشارة دخول المؤشر بمغلف بحيث يتم استدعاءه
                // عند دخول المؤشر و ذلك لتغيير لون الإطار إلى قيمته الأصلية
                onMouseEnter.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                    this.style.borderColor = Color(0, 0, 0);
                });
                // نقوم بربط إشارة خروج المؤشر بمغلف بحيث يتم استدعاءه
                // عند خروج المؤشر و ذلك لتغيير لون الإطار إلى الأحمر لتوضيح أننا خارج المرسم
                onMouseOut.connect(closure (widget: ref[Widget], payload: ref[Int]) {
                    this.style.borderColor = Color(255, 0, 0);
                });
                // نقوم بربط إشارة بدء الضفط على المؤشر بمغلف بحيث يتم استدعاءه
                // عند بدء الضفط على المؤشر و ذلك لتغيير قيمة المتغير الخاص بتحديد فيما إذا تم
                // الضغط على المؤشر إلى القيمة المناسبة و ذلك فقط إذا تم الضغط على الزر الأيسر
                onMouseDown.connect(closure (pressed: by_ref)&(widget: ref[Widget], payload: ref[MouseButtonPayload]) {
                    if payload.button == 0 pressed = 1;
                });
                // نقوم بربط إشارة نهاية الضفط على المؤشر بمغلف بحيث يتم استدعاءه
                // عند نهاية الضفط على المؤشر و ذلك لتغيير قيمة المتغير الخاص بتحديد فيما إذا تم
                // الضغط على المؤشر إلى القيمة المناسبة و ذلك فقط إذا تم إيقاف الضغط على الزر الأيسر
                onMouseUp.connect(closure (pressed: by_ref)&(widget: ref[Widget], payload: ref[MouseButtonPayload]) {
                    if payload.button == 0 pressed = 0;
                });
                // نقوم بربط إشارة تغيير الحجم بمغلف بحيث يتم استدعاءه
                // عند تغيير الحجم و ذلك لتغيير الأبعاد فيما يتناسب مع تغير الحجم
                onResize.connect(closure (dim: by_ref)&(widget: ref[Widget], payload: ref[Int]) {
                    dim = this.getDimensions();
                });
            }
        });
    });

    // سنقوم فيما يلي بتعريف ما يلزم من أجل استعمال المقبض مع المرسم
    Window.instance.{
        // المفاتيح التي نريدها هي الاتجاهات فقط
        keysToSwallow = "ArrowUp,ArrowDown,ArrowLeft,ArrowRight";
        // نقوم بربط إشارة بدء الضغط على المؤشر بمؤجل يقوم عند استدعاءه
        // بتحديد التغير في الموقع بناء على الزر المضغوط
        onKeyDown.connect(closure (
            delta: by_ref, pointerLocked: by_ref, fullscreen: by_ref
        )&(
            window: ref[Window], payload: ref[String]
        ) {
            if payload == "ArrowUp" delta.y = -5
            else if payload == "ArrowDown" delta.y = 5
            else if payload == "ArrowRight" delta.x = 5
            else if payload == "ArrowLeft" delta.x = -5
            // كما يقوم هنا بالدخول إلى وضع ملء الشاشة أو الخروج منه
            else if payload == "Enter" {
                if fullscreen exitFullScreen()
                else canvas.requestFullScreen();
            }
            // أيضاً نقوم بطلب قفل المؤشر أو الخروج منه
            else if payload == "Space" {
                if pointerLocked exitPointerLock()
                else canvas.requestPointerLock();
            }
        });
        // عند توقف الضغط على المؤشر نقوم بتصفير قيم الحركة
        onKeyUp.connect(closure (delta: by_ref)&(window: ref[Window], payload: ref[String]) {
            if payload == "ArrowUp" delta.y = 0
            else if payload == "ArrowDown" delta.y = 0
            else if payload == "ArrowRight" delta.x = 0
            else if payload == "ArrowLeft" delta.x = 0;
        });
        // نقوم بربط إشارة قفل المؤشر بمؤجل عند تنفيذه يتم تحديث قيمة المتغير الذي نحدد
        // ضمنه فيما إذا كنا في وضع غلق المؤشر أم لا
        onPointerLockChange.connect(closure (pointerLocked: by_ref)&(window: ref[Window], payload: ref[Bool]) {
            pointerLocked = payload;
        });
        // نقوم بربط إشارة تغير وضع ملء الشاشة بمؤجل عند تنفيذه يتم تحديث قيمة المتغير
        // الذي نخزن ضمنه فيما إذا كنا في وضع ملء الشاشة أم لا
        // كما أننا نقوم بقفل المؤشر عند الدخول في وضع ملء الشاشة و الخروج منه عكس ذلك
        onFullScreenChange.connect(closure (fullscreen: by_ref)&(window: ref[Window], payload: ref[Bool]) {
            fullscreen = payload;
            if payload canvas.requestPointerLock()
            else exitPointerLock();
        });
        // نقوم بربط إشارات ربط المقيض و فصله بمؤجل عند تنفيذ يتم تحديث
        // قيمة المتغير الذي يخزن عدد المقابض الحالية
        onGamepadConnected.connect(closure (gamepadsCount: by_ref)&(window: ref[Window], payload: ref[String]) {
            gamepadsCount = getGamepadsCount();
        });
        onGamepadDisconnected.connect(closure (gamepadsCount: by_ref)&(window: ref[Window], payload: ref[String]) {
            gamepadsCount = getGamepadsCount();
        });
    };

    // توليد صورة بشكل ديناميكي
    def dynImg: ImageResource;  // مورد الصورة
    def canvasRes: CanvasResource(100, 100);  // المرسم الذي سنرسم عليه
    canvasRes.setLineWidth(5);
    canvasRes.setStrokeStyle("#ff5555");
    // نقوم برسم خطين على المرسم
    canvasRes.drawLine(0, 0, 100, 100);
    canvasRes.drawLine(100, 0, 0, 100);
    // نقوم برسم مضلع على المرسم
    def points: Array [Int]({ 25, 25, 75, 25, 75, 75, 25, 75 });
    canvasRes.drawPolygon(4, points.buf, false);
    // نقوم بتحميل الصورة من المرسم
    dynImg.initFromCanvas(canvasRes);

    // حلقة التحريك
    def x: Int = -80;
    // نبدأ مؤقت يقوم بتنفيذ ما نريد كل 8 ثواني
    startTimer(8000000, closure (
        x: by_ref, img: by_ref, dynImg: by_ref, pos: by_ref, delta: by_ref, pressed: by_ref, gamepadsCount: by_ref
    ) & (json: Json) {
        // في كل مرة نقوم بمحو الرسومات للبدء من جديد
        canvas.clear();
        canvas.drawLine(0, 0, 100, 100);  // نرسم خط
        canvas.drawCircle(100, 180, 90);  // نرسم دائرة
        // نقوم برسم مضلع
        def points: Array [Int]({ 50, 50, 100, 50, 100, 100, 50, 100, 25, 75 });
        canvas.setFillStyle("black", "blue", 0, 0, 100, 100);
        canvas.drawPolygon(5, points.buf, true);
        // نرسم نص
        def TEXT: "Alusus";
        canvas.drawText(TEXT, "30px AlususMono", 550, 150);

        def size: Int = Math.sin(3.14 * x / 320.0) * 40 + 100;
        def y1: Int = Math.sin(3.14 * x / 320.0) * 100 + 400 - size / 2;
        def y2: Int = Math.cos(3.14 * x / 320.0) * 100 + 400 - size / 2;
        def alpha: Float = Math.sin(3.14 * x / 80.0) / 4 + 0.75;
        canvas.drawImage(img, x, y1, size, size, alpha);
        canvas.drawImage(dynImg, x, y2, size, size, alpha);

        // في كل مرة في حال كان لدينا مقابض نقوم بجلب الحركة التي تمت بواسطتها
        // و نقوم بالتحريك كما يلزم
        if gamepadsCount > 0 {
            delta.x = getGamepadAxis(0, 0) * 5;
            delta.y = getGamepadAxis(0, 1) * 5;
            pressed = getGamepadButton(0, 0) > 0.5;
        }

        if pressed canvas.drawImage(img, pos.x, pos.y, size, size, alpha)
        else canvas.drawImage(dynImg, pos.x, pos.y, size, size, alpha);
        if ++x >= 1280 x = -80;
        pos.x += delta.x;
        pos.y += delta.y;
    });

    runEventLoop();
}


Console.print("Starting server on port 8010...\nURL: http://localhost:8010/\n");
// نبدأ تشغيل السيرفر على المنفذ 8010
buildAndRunServer(Array[CharsPtr]({ "listening_ports", "8010", "static_file_max_age", "0" }));