Skip to main content
تخط المحتوى

التخطيطات، وعروض القوائم، والبطاقات

معرفة الطرق المختلفة لوضع الأشياء على الصفحة

تخطيط المحتوى الخاص بك

الآن بعد أن فهمنا كيفية عمل الصفحات، حان الوقت لإضافة أشياء إليها. سنستعرض عددًا من مكونات وعناصر التخطيط المهمة التي ستكون مفيدة عند تصميم تطبيقنا.

بنهاية هذا القسم، سيكون لديك تطبيق ذو مظهر أنيق.

قوائم العرض

إن استخدمت يومًا ديسكفر أو نيو تشات أو إعدادات نظام بلازما، فستكون قد صادفت قائمة عرض. ببساطة، تتيح لك عرض البيانات في قائمة.

pageStack.initialPage: Kirigami.ScrollablePage {
    // ...
    Kirigami.CardsListView {
       id: cardsView
        model: kountdownModel
        delegate: kountdownDelegate
    }
}

يبدو هذا غامضًا، لكن لا تقلق. لنبدأ من البداية.

نُضِف هذا المكون داخل صفحة قابلة للتمرير من كيريغامي من الدرس السابق.

نستخدم قائمة عرض بطاقات كيريغامي، وهي قائمة عرض تتيح لنا عرض البطاقات بسهولة في قائمة. لكن قوائم العرض صُممت لعرض بيانات مأخوذة من نموذج - لتعبئة نفسها آليًا من مجموعة بيانات نشير إليها. وهنا تأتي خاصية النموذج: في هذا المثال، تشير إلى kountdownModel.

النموذج

Kirigami.ApplicationWindow {
    // ...
    ListModel {
        id: kountdownModel
        // كل عنصر قائمة هو عنصر في القائمة، يحوي معلومات
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }
    // ...
}

نُضِف kountdownModel داخل نافذة تطبيق كيريغامي من الدرس السابق.

يُعرّف النموذج طريقة بنية إدخال البيانات. سيتكون kountdownModel من عنصر واحد فقط حاليًا. بالنظر إلى عنصر القائمة أعلاه، نرى كيف بُنيت بيانات kountdownModel: يحوي اسمًا ووصفًا وتاريخًا. هذا ليس ثابتًا، وقد يكون لديك أنواع مختلفة من البيانات في نموذجك. الأولان مجرد سلاسل نصية، والثالث رقم نستخدمه كعنصر نائب.

النماذج مفيدة أيضًا في كيفية تعديلها باستخدام عدة دوال. بعض الدوال المهمة هي:

  • ListModel.append(yourobject: jsobject) يُضيف كائن جافا سكريبت yourobject إلى نموذج القائمة ويضعه بعد آخر عنصر في النموذج. ليحدث هذا بشكل صحيح، يجب توفير كائن جافا سكريبت بالخصائص الصحيحة وأنواع البيانات المقابلة.
  • ListModel.get(index: int) يُرجع كائن جافا سكريبت في موقع الفهرس الذي توفره.
  • ListModel.remove(index: int, count: int) يُزيل كائن جافا سكريبت في موقع index المقدم، وعددًا بعده بقدر ما تضعه في count (1 يشمل فقط كائن جافا سكريبت في الفهرس المقدم)
  • ListModel.set(index: int, yourobject: jsobject) يُغير العنصر في موقع index المقدم بالقيم المقدمة في yourobject. نفس قواعد append().

المفوّضون

بينما يحوي kountdownModel البيانات التي ستُعرض، سيتولى kountdownDelegate كيفية عرض البيانات في قائمة العرض. لذلك نستخدم قائمة عرض بطاقات كيريغامي المصممة لعرض مفوّضين من نوع بطاقة، وسيُمثل هؤلاء المفوّضون بصريًا عبر بطاقة مجردة من كيريغامي.

يتلقى المفوّضون آليًا خصائص مثيلات عنصر القائمة التي حددناها في نموذجنا. لذلك يمكننا الإشارة إلى خصائص name و description و date كما لو كانت متغيرات تقليدية داخل مفوّضنا.

بناء بطاقة المفوّض

يمكن إضافة المكون الذي سيمثل مفوّضنا داخل نافذة تطبيق كيريغامي. سنتحقق بعد ذلك مما يفعله كل جزء من مكون المفوّض.

Kirigami.ApplicationWindow {
    // ...
    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                // يُعرّف العرض/الارتفاع الضمني العرض/الارتفاع الطبيعي لعنصر إذا لم يُحدد عرض أو
                // ارتفاع. يُعرّف الإعداد أدناه الحجم المفضل للمكون بناءً على محتواه
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        level: 1
                        text: date
                    }

                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        Controls.Label {
                            Layout.fillWidth: true
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        Layout.columnSpan: 2
                        text: i18n("Edit")
                        // عند النقر: سيُفعل... قريبًا!
                    }
                }
            }
        }
    }
    // ...
}

العرض الضمني والارتفاع الضمني

الجزء الأول الذي سننظر إليه هو كيفية إدارة عرض وارتفاع مكوننا:

Kirigami.AbstractCard {
    contentItem: Item {
        implicitWidth: delegateLayout.implicitWidth
        implicitHeight: delegateLayout.implicitHeight
        GridLayout {
            id: delegateLayout
            // ...
        }
    }
}

بالنظر إلى بطاقتنا المجردة من كيريغامي، أول الخصائص التي نضبطها هي العرض الضمني و الارتفاع الضمني. ضبطناها على delegateLayout.implicitWidth و delegateLayout.implicitHeight، أي العرض الضمني و الارتفاع الضمني لعنصر التخطيط الشبكي.

العروض والارتفاعات الضمنية هي خصائص متاحة في أي عنصر تعمل كتلميحات وتُضبط كإعداد مبدئي، أو كخيار احتياطي، إذا لم يُضبط عرض أو ارتفاع صريح لهذه المكونات. هذه القيم مبدئيًا 0x0، لذا من المهم جدًا تعريفها في مكونات العنصر الخام كما فعلنا أعلاه.

هنا ضبطنا implicitWidth و implicitHeight لـ بطاقتنا المجردة من كيريغامي على تلك الخاصة بـ التخطيط الشبكي أدناه لضمان عدم تجاوزها للبطاقة. بهذه الطريقة، تأخذ البطاقة المساحة اللازمة لمحتوياتها.

التخطيطات

التخطيط الشبكي داخل مكون العنصر الذي قدمناه لخاصية عنصر المحتوى. هذا هو العنصر الذي يحوي ما سيُعرض في بطاقتك.

نحتاج أيضًا لاختيار تخطيط لمكوناتنا حتى لا تتراكم فوق بعضها. هناك ثلاثة أنواع رئيسية يمكن الاختيار من بينها:

  • ColumnLayout يُرتّب مكوّناتك عموديًا، في عمود واحد
  • RowLayout يُرتّب مكوّناتك أفقيًا، في صف واحد
  • GridLayout يُرتّب مكوّناتك في شبكة بتوليفة من اختيارك

مع ColumnLayout وRowLayout، كل ما علينا فعله هو كتابة مكوّناتنا داخل مكوّن التخطيط. كما ترى، اخترنا GridLayout، الذي يتطلب جهدًا يدويًا أكثر قليلًا.

GridLayout {
    id: delegateLayout
    anchors {
        left: parent.left
        top: parent.top
        right: parent.right
    }
    rowSpacing: Kirigami.Units.largeSpacing
    columnSpacing: Kirigami.Units.largeSpacing
    columns: root.wideScreen ? 4 : 2
    // ...
}

أول ما تراه هو anchors لدينا. نظام التثبيت في QtQuick يُوفّر طريقة مفيدة لضمان وضع مكوّناتك في أجزاء معيّنة من المكوّن الأب. ثبّتنا GridLayout إلى اليسار والأعلى واليمين من البطاقة الأب، مما يضمن امتداد محتوانا عبر البطاقة بأكملها.

بعد ذلك نحدد التباعد بين الصفوف والأعمدة داخل شبكتنا، حتى لا تتزاحم مكوّناتنا. يُوفّر كيريجامي عددًا من الوحدات المحددة مسبقًا المفيدة لاستخدامها لهذا الغرض:

وحدة كيريجاميبكسل
smallSpacing4px
largeSpacing8px
gridUnit18px

كما قد تتذكر، root هو مُعرّف Kirigami.ApplicationWindow خاصتنا. يُوفّر الخاصية wideScreen، المُستخدمة لتحديد ما إذا كانت شاشة الجهاز الحالي عريضة (أي شاشة حاسوب أو هاتف في الوضع الأفقي). نستخدم شرطًا ثلاثيًا هنا لتغيير عدد الأعمدة في شبكتنا اعتمادًا على الشاشة التي نستخدمها: إذا كانت عريضة، ستحتوي الشبكة على 4 أعمدة، وإلا فستحتوي على 2.

المكوّنات الداخلية

قد نكتفي بإنشاء ثلاث وسوم داخل مكوّن المفوّض وننتهي، لكن ذلك لن يبدو جميلًا بشكل خاص. سنستفيد من بعض المكوّنات الأكثر ملاءمة:

GridLayout {
    // ...
    Kirigami.Heading {
        Layout.fillHeight: true
        level: 1
        text: date
    }

    ColumnLayout {
        Kirigami.Heading {
            Layout.fillWidth: true
            level: 2
            text: name
        }

        Kirigami.Separator {
            Layout.fillWidth: true
            visible: description.length > 0
        }

        Controls.Label {
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            text: description
            visible: description.length > 0
        }
    }

    Controls.Button {
        Layout.alignment: Qt.AlignRight
        Layout.columnSpan: 2
        text: i18n("Edit")
    }
}
كيف تبدو البطاقة المخصصة

كيف تبدو البطاقة المخصصة

  • يسارًا، Kirigami.Heading: يستخدم date الخاص بـ ListElement كعنوان من المستوى 1.
  • وسطًا، ColumnLayout: يحتوي على Kirigami.Heading يعرض اسم المهمة؛ وKirigami.Separator، الذي يُوفّر الخط الأفقي؛ وControls.Label، الذي يعرض وصفًا اختياريًا للمهمة. المكوّنان الأخيران لهما خاصية visible، التي تتحقق مما إذا كان الوصف فارغًا أم لا وتعرض المكوّنين اعتمادًا على نتيجة description.length > 0.
  • يمينًا، Controls.Button: زر سيفعل شيئًا... قريبًا!

تطبيقنا حتى الآن

Main.qml:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    id: root

    width: 400
    height: 300

    title: i18nc("@title:window", "Day Kountdown")

    // ListModel needed for ListView, contains elements to be displayed
    ListModel {
        id: kountdownModel
        // Each ListElement is an element on the list, containing information
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }

    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        // Level determines the size of the heading
                        level: 1
                        text: date
                    }

                    // Layout for positioning elements vertically
                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        // Horizontal rule
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        // Labels contain text
                        Controls.Label {
                            Layout.fillWidth: true
                            // Word wrap makes text stay within box and shift with size
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        // Column spanning within grid layout (vertically in this case)
                        Layout.columnSpan: 2
                        text: i18n("Edit")
                        //onClicked: to be done...
                    }
                }
            }
        }
    }

    pageStack.initialPage: Kirigami.ScrollablePage {
        title: i18nc("@title", "Kountdown")

        // List view for card elements
        Kirigami.CardsListView {
            id: cardsView
            // Model contains info to be displayed
            model: kountdownModel
            // Delegate is how the information will be presented in the ListView
            delegate: kountdownDelegate
        }
    }
}

لقطة شاشة لمظهر التطبيق بعد إكمال هذا الدرس

إذن هذه هي بطاقتنا الأساسية!

بهذه الخطوات، وضعنا الآن الأساس لجميع الوظائف التي سنضيفها إلى تطبيقنا.