0Pricing
Learn Rust Coding · درس

إدارة حالة التطبيق

احتفظ ببيانات واجهة المستخدم وحدّثها

إدارة حالة التطبيق درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.

الحالة في بنية App

في egui، تكون حالة تطبيقك عبارة عن بيانات Rust عادية موجودة في البنية التي تطبّق eframe::App. لا يوجد مخزن خاص أو نموذج تديره المنصة.

تحتوي الحقول على كل شيء: مدخلات النماذج، والتحديدات، والبيانات المحمّلة، والرايات. في كل إطار، تقرأ update هذه الحقول وتعدّلها مباشرةً عبر &mut self.

struct App {
    query: String,
    results: Vec<Item>,
    selected: Option<usize>,
    loading: bool,
}

اشتقاق Default

تنشئ eframe تطبيقك داخل دالة إغلاق تُمرَّر إلى run_native. أبسط نمط هو استخدام #[derive(Default)] وإنشاء التطبيق باستخدام App::default().

بالنسبة إلى القيم الابتدائية غير الافتراضية، طبّق Default يدويًا أو أضف مُنشئًا يستقبل CreationContext لإجراء إعدادات مثل الخطوط أو الاستمرارية.

impl App {
    fn new(cc: &eframe::CreationContext) -> Self {
        let mut app = Self::default();
        app.dark = cc.egui_ctx.style().visuals.dark_mode;
        app
    }
}

المصدر الوحيد للحقيقة

نظرًا إلى أن عناصر الواجهة تستعير &mut للوصول إلى الحقول التي تستند إليها، تبقى البنية دائمًا المصدر الوحيد للحقيقة. لا توجد حالة عرض منفصلة يجب إبقاؤها متزامنة.

يعكس مربع اختيار مرتبط بـ &mut self.enabled قيمة bool تلك ويحدّثها مباشرةً — فإذا قرأتها في أي موضع آخر ضمن الإطار نفسه، فسترى القيمة الحالية.

ui.checkbox(&mut self.enabled, "Enabled");
if self.enabled {
    ui.label("Feature is on");
}

تأجيل التعديلات

توجد مشكلة استعارة دقيقة: غالبًا ما تكرّر عناصر Vec بينما تريد حذف عنصر منها. لا يمكنك تعديل المتجه ما دامت هناك استعارة مشتركة نشطة.

الحل هو تسجيل الإجراء المقصود في متغير محلي أثناء الحلقة، ثم تطبيقه بعد انتهاء الحلقة.

let mut to_remove = None;
for (i, item) in self.items.iter().enumerate() {
    ui.horizontal(|ui| {
        ui.label(&item.name);
        if ui.button("x").clicked() { to_remove = Some(i); }
    });
}
if let Some(i) = to_remove { self.items.remove(i); }

الشاشات المعتمدة على التعدادات

في التطبيقات متعددة الشاشات، مثّل العرض الحالي بحقل من نوع تعداد. تطابق دالة update قيمته لتحديد ما يجب رسمه.

يحافظ ذلك على وضوح التنقل وشموله — إذ يجبرك المصرّف على معالجة كل شاشة، ويزيل فئات كاملة من أخطاء التوجيه.

enum Screen { Home, Settings, About }

match self.screen {
    Screen::Home => self.draw_home(ui),
    Screen::Settings => self.draw_settings(ui),
    Screen::About => self.draw_about(ui),
}

ذاكرة egui لحالة الواجهة

تتعلق بعض الحالات بالواجهة فقط، لا بمجال تطبيقك — مثل الأقسام المطوية، ومواضع التمرير، وحالة السحب. تخزّن egui هذه الحالات في ctx.memory، مع ربطها بـ Id الخاص بعنصر الواجهة.

نادرًا ما تحتاج إلى التعامل معها مباشرةً، لكن ctx.data_mut يتيح لك تخزين قيم صغيرة خاصة بكل معرّف، تستمر بين الإطارات من دون إثقال بنية تطبيقك.

let id = egui::Id::new("my_toggle");
let mut open = ctx.data_mut(|d| d.get_temp::<bool>(id).unwrap_or(false));
ui.checkbox(&mut open, "Open");
ctx.data_mut(|d| d.insert_temp(id, open));

العمل في الخلفية باستخدام القنوات

لا تحجب update بعمليات إدخال وإخراج بطيئة — فهذا يجمّد حلقة 60 إطارًا في الثانية. أنشئ خيطًا وتواصل معه عبر قناة std::sync::mpsc.

يرسل العامل النتائج إلى الخلف؛ وتفرّغ update مستقبل القناة دون حجب باستخدام try_recv في كل إطار. استدعِ ctx.request_repaint() من العامل كي تستيقظ الواجهة عند وصول البيانات.

if let Ok(msg) = self.rx.try_recv() {
    self.results = msg;
    self.loading = false;
}

إنشاء العامل

استنسخ Context والمرسل إلى داخل الخيط. النسخ غير مكلف — إذ إنه Arc داخليًا — ويسمح للعامل بطلب إعادة الرسم.

انقل ملكية مدخلات العمل إلى دالة الإغلاق لتلبية متطلبات مدقّق الاستعارة وتجنّب مشكلات الأعمار.

let tx = self.tx.clone();
let ctx = ctx.clone();
std::thread::spawn(move || {
    let data = expensive_load();
    let _ = tx.send(data);
    ctx.request_repaint();
});

استمرارية الحالة

يمكن لـ eframe إجراء تسلسل لتطبيقك بين مرات التشغيل. فعّل ميزة persistence، واشتق serde::Serialize / Deserialize، وطبّق App::save.

عند بدء التشغيل، اقرأ الحالة المحفوظة من cc.storage في المُنشئ، مع الرجوع إلى القيم الافتراضية عند عدم وجود بيانات مخزنة.

fn save(&mut self, storage: &mut dyn eframe::Storage) {
    eframe::set_value(storage, eframe::APP_KEY, self);
}
// in new():
if let Some(s) = cc.storage {
    if let Some(app) = eframe::get_value(s, eframe::APP_KEY) { return app; }
}

إبقاء update خفيفة

بما أن update تعمل في كل إطار، فتجنّب إعادة حساب البيانات المشتقة المكلفة داخلها. خزّن النتائج مؤقتًا في الحقول، وأعد حسابها فقط عند تغيّر المدخلات.

من الأنماط الشائعة: تتبّع راية dirty تضبطها Response::changed()، ثم إعادة بناء ذاكرات التخزين المؤقت في بداية update فقط عندما تكون الراية مضبوطة.

if self.dirty {
    self.filtered = self.filter_items();
    self.dirty = false;
}
for item in &self.filtered { ui.label(&item.name); }

الملكية تحافظ على وضوح الحالة

الفكرة المتكررة هي أن egui تعتمد على ملكية Rust بدلًا من أطر العمل لإدارة الحالة. تملك بنيتك البيانات، ويمنح &mut self وصولًا حصريًا في كل إطار، وتتواصل الخيوط عبر القنوات.

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

تحقق سريع

كيف ينبغي لك تنفيذ جلب بطيء من الشبكة؟

مراجعة

تتكون حالة التطبيق من حقول عادية في بنية eframe::App، وتُعدَّل عبر &mut self في كل إطار، مما يجعل البنية المصدر الوحيد للحقيقة.

أجّل التعديلات أثناء التكرار، ومثّل الشاشات بالتعدادات، وانقل العمل البطيء إلى خيوط باستخدام القنوات، واحفظ الحالة باستخدام مخزن eframe. أبقِ update خفيفةً عبر تخزين البيانات المشتقة مؤقتًا خلف رايات dirty. التالي: حزم تطبيقك للتوزيع.

الأسئلة الشائعة

هل درس «إدارة حالة التطبيق» مجاني؟

نعم — نص درس «إدارة حالة التطبيق» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.

ماذا ستتعلم في «إدارة حالة التطبيق»؟

احتفظ ببيانات واجهة المستخدم وحدّثها تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟

لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «إدارة حالة التطبيق»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟

نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساسيات واجهة المستخدم Immediate-Mode
  2. الأدوات والتخطيط
  3. إدارة حالة التطبيق
  4. تجهيز تطبيق سطح المكتب
← العودة إلى Learn Rust Coding