تُرجمت هذه الصفحة آليًا وقد تحتوي على أخطاء. اقرأ الأصل الإنجليزي

التوثيق

التعليمات البرمجية الخاصة بكل منصة

تعمل حزمة NSH واحدة على iOS وAndroid وmacOS وWindows وLinux (وفي Mac Catalyst)، وفي المتصفحات عبر بيئة تشغيل الويب. لا يحتاج معظم التطبيق إلى أي فحوصات للمنصة: فالعناصر والأنماط والتخطيط والوحدات تتصرف بالطريقة نفسها في كل مكان، والمكونات التي تبنيها منها تبدو متطابقة في كل مكان. وعندما يكون شيء ما خاصًا بمنصة واحدة فعلًا، يمنحه NSH مكانًا واضحًا، بحيث تبقى شيفرة المنصة ظاهرة ومحصورة:

RustTSXالاستخدام
دوال البناء (.ripple(..)، .input_accessory(..))ios={{…}}، android={{…}}، desktop={{…}}، web={{…}}الخصائص التي تتوفر على منصة واحدة فقط.
on_back_press، nsh_core::element::desktopnsh/mobile، nsh/desktopواجهات برمجية تتوفر على المنصات المحمولة فقط أو على أجهزة سطح المكتب فقط.
os()، idiom()، is_desktop()platform.os، platform.desktop، platform.selectاختيار القيم أو الشيفرة حسب المنصة.
.when(os() == Os::Ios, ..)فئات ios: / android: / web:أنماط خاصة بكل منصة.

#سطح المكتب والجوال

كل منصة هي إما سطح مكتب أو جوال، وفق قاعدة واحدة. سطح المكتب هو macOS وWindows وLinux، والمتصفحات بنمط سطح المكتب (فأرة ونافذة عريضة، الويب). أما الجوال فهو iOS وAndroid، والمتصفحات بنمط الهاتف أو الجهاز اللوحي؛ ويشغّل Mac Catalyst واجهة iOS الخلفية، فهو جوال أيضًا، مع خصائص ios={{…}}. وتتبع هذه القاعدة خصائص desktop={{…}} وplatform.select({ desktop, mobile }) وplatform.desktop وplatform.idiom === "desktop" جميعها، وكذلك مكونات nsh/base (القائمة نافذة منبثقة على أجهزة سطح المكتب وورقة سفلية على الجوال)، وفي Rust أيضًا is_desktop().

أما isAvailable في nsh/desktop فيجيب عن سؤال أضيق: هل تعمل واجهات النافذة والقوائم في تلك الوحدة، وهي تعمل على macOS وWindows وLinux. استخدم platform.desktop للتحكم في التخطيط والكثافة والإدخال، واستخدم isAvailable للتحكم في استدعاءات Window وMenu.

#الخصائص الخاصة بمنصة واحدة

في Rust، الخاصية التي تتوفر على منصة واحدة هي دالة بناء كأي دالة أخرى (.ripple(..)، و.elevation(..) لـ Android، و.input_accessory(..) لـ iOS، و.window_drag(..) لأجهزة سطح المكتب)؛ وتتجاهلها المنصات الأخرى. أما في TSX فتُجمع الخصائص الموجودة على منصة واحدة فقط في كائن يحمل اسمها. يقبل كل عنصر ios وandroid وdesktop (macOS وWindows وLinux، والمتصفحات بنمط سطح المكتب) وweb (المتصفحات)؛ وعلى المنصات الأخرى يُتجاهل الكائن، وعلى منصته الخاصة تُطبَّق مفاتيحه وحدها. ويطبّق متصفح سطح المكتب كلًّا من desktop وweb.

use nsh_base::dialog;
use nsh_core::{
    Ripple,
    element::{AutoFill, Orientation}, prelude::*,
};

view()
    .on_press(open)
    .active(|s| s.opacity(0.6))
    .ripple(Ripple {
        color: Some(rgba(0x0000001f).into()),
        ..Ripple::default()
    })
    .elevation(px(4.0))
    .p_4()
    .rounded(px(12.0))
    .bg(white())
    .child(text("Open"))

input().placeholder("Code").auto_fill(AutoFill::OneTimeCode)

when(shown, || {
    dialog()
        .supported_orientations([Orientation::Portrait])
        .status_bar_translucent(true)
        .child(…)
})

كائنات TSX تفاعلية كأي خاصية؛ والمفتاح الذي يُحذف من الكائن يُمسح أثره.

ما هو متوفر حاليًا:

RustTSXعلىالوصف
.ripple(Ripple { .. })android={{ ripple }}كل عنصرتأثير التموّج (ripple) المادي أثناء الضغط: اللون، وبلا حدود، ونصف القطر، والمقدمة.
.elevation(..)android={{ elevation }}كل عنصرالظل المادي، بالنقاط، انطلاقًا من شكل العرض.
android={{ accessibilityLiveRegion }}كل عنصر"none" أو "polite" أو "assertive": يعلن TalkBack عن تغيّرات العرض.
.status_bar_translucent(..)android={{ statusBarTranslucent }}Dialogيمتد محتوى مربع الحوار أسفل شريط الحالة.
.input_accessory(..)ios={{ inputAccessoryViewID }}InputInputAccessoryView الذي يظهر فوق لوحة المفاتيح.
.auto_fill(..)ios={{ textContentType }}Inputأسماء iOS لأنواع الملء التلقائي (AutoFill في Rust يغطي المنصتين).
.supported_orientations(..)ios={{ supportedOrientations }}Dialogالاتجاهات التي يسمح بها.
.back_forward_gestures(..)ios={{ allowsBackForwardNavigationGestures }}WebViewالسحب للرجوع والتقدم في سجل الصفحات.
.header_right_system_item(..)ios={{ headerRightSystemItem }}NavPageزر شريط UIKit الأصلي في نهاية الترويسة، ويستدعي عند الضغط headerRight.
.header_left_system_item(..)ios={{ headerLeftSystemItem }}NavPageزر شريط UIKit الأصلي في بداية الترويسة، ويستدعي عند الضغط headerLeft.
.header_right_symbol(..)ios={{ headerRightSymbol }}NavPageزر شريط UIKit الأصلي الذي يعرض SF Symbol في نهاية الترويسة، ويستدعي عند الضغط headerRight.
.header_left_symbol(..)ios={{ headerLeftSymbol }}NavPageزر شريط UIKit الأصلي الذي يعرض SF Symbol في بداية الترويسة، ويستدعي عند الضغط headerLeft.
.back_forward_gestures(..)desktop={{ allowsBackForwardNavigationGestures }}WebViewعلى macOS: تُرجع حركات السحب بإصبعين على لوحة اللمس وتُقدِّم في سجل الصفحات؛ وعلى Windows: حركات السحب على شاشة اللمس؛ وعلى Linux: حركات السحب على لوحة اللمس.
.window_drag(..)desktop={{ windowDrag }}كل عنصرالضغط عليه يسحب النافذة، كما يفعل شريط العنوان في النظام (انظر nsh/desktop).

في TSX، يُقبل desktop={{…}} وweb={{…}} في كل مكان. وتخبر الأنواع (IOSProps وAndroidProps وDesktopProps وصيغها الخاصة بكل عنصر) محرّرك بما يقبله كل عنصر.

ما يعنيه العنصر على كل المنصات هو خاصية من المستوى الأعلى (وفي Rust دالة بالاسم نفسه)، وتُطبَّق حيث يكون لها معنى: فـ title وheaderStyle وheaderLargeTitle وheaderBackTitle في NavPage تُعدّ ترويسة النظام في iOS وAndroid، وخانتها header هي ترويسة أجهزة سطح المكتب والويب، وpresentation في Dialog تختار أوراق iOS السفلية. أما closeOnBackdropPress وpagingEnabled وrefreshing وhitSlop وغيرها في AlertDialog.show فتعمل على كل المنصات. وميزة النظام الخاصة بمنصة تُوضع في كائنها، وعندما تتوفر على عدة منصات توضع في كائن كل منها بالاسم نفسه: فـ allowsBackForwardNavigationGestures في WebView، وهي حركات النظام للتنقل في سجل الصفحات، موجودة في ios={{…}} وdesktop={{…}}.

#وحدات المنصات

في Rust، الواجهات التي تتوفر على منصة واحدة فقط هي دوال في nsh_core::prelude (on_back_press، input_accessory_view) وفي nsh_core::element::desktop (النافذة: set_title، set_title_bar، title_bar، minimize، toggle_maximize، toggle_full_screen، set_frame، set_level، order_front، close_window؛ والقوائم: set_application_menu، popup_menu)؛ وفي غيرها لا تفعل شيئًا. وفي TSX، تحوي nsh/mobile واجهات iOS وAndroid (بعضها يخص منصة واحدة)، وتحوي nsh/desktop واجهات أجهزة سطح المكتب. يمكن استيراد الاثنتين على كل المنصات، فتعمل حزمة واحدة في كل مكان: على المنصات الأخرى تعود دوالهما فورًا ولا تشغل مكوناتهما أي مساحة. وتصدّر كل منهما isAvailable، وهي true على منصاتها الخاصة؛ ويميّز platform.os بينهما.

#nsh/mobile

isAvailable هي true على iOS (مع Mac Catalyst) وAndroid. وتتوفر على المنصتين TabBar، وهي شريط علامات التبويب في النظام.

#BackHandler (Android)

RustTSXالوصف
on_back_press(listener)BackHandler.on("press", listener)يستمع إلى زر الرجوع بعد مربعات الحوار وقبل المكدسات؛ أعد true عند المعالجة. يرتبط المستمع بالمكون (أو التأثير) الذي أضافه؛ وفي TSX يعيد الاستدعاء أيضًا دالة توقفه مبكرًا.
BackHandler.exitApp()يغادر التطبيق.
use nsh_core::prelude::*;

fn player() -> impl IntoElement {
    let full_screen = signal(false);
    // Back leaves full screen before it leaves the page. The listener goes
    // with the view.
    on_back_press(move || {
        if !full_screen.get_untracked() {
            return false;
        }
        full_screen.set(false);
        true
    });
    // …
}

يعالج مربع الحوار العلوي زر الرجوع أولًا، ثم مستمعو الرجوع (الأحدث أولًا)، ثم مكدس التنقل العلوي، ويخرج التطبيق إذا لم يعالجه أحد. انظر التنقل.

#InputAccessoryView (iOS)

RustTSXالوصف
input_accessory_view(native_id)InputAccessoryViewشريط فوق لوحة المفاتيح لحقول الإدخال التي تذكر معرّفها الأصلي (.input_accessory(..)، ios={{ inputAccessoryViewID }}).

انظر النماذج ولوحة المفاتيح.

#nsh/desktop

ترسم تطبيقات Rust أشرطة العنوان الخاصة بها باستخدام nsh_core::element::desktop (البناء باستخدام Rust). وهذه وحدة TSX:

التصديرالوصف
Windowالنافذة: setTitle، وsetTitleBar({ style: "hidden" }) لشريط عنوان خاص بالتطبيق، وminimize، وtoggleMaximize، وtoggleFullScreen، وclose.
titleBarارتفاع شريط العنوان المخفي والمساحة التي تشغلها عناصر تحكم النافذة في النظام (تفاعلي).
MenusetApplicationMenu(items) (شريط القوائم، مع الاختصارات) وpopup(items, at?) (القوائم السياقية).
isAvailabletrue حيث تعمل واجهات النافذة والقوائم: macOS وWindows وLinux. أما متصفحات سطح المكتب فلها platform.desktop وتحتفظ بنافذة المتصفح وقوائمه.

انظر nsh/desktop.

#شريط الحالة

على المنصتين، يتبع شريط الحالة الصفحة أو مربع الحوار العلوي، كما في UIKit حيث يقرره متحكم العرض الأعلى: NavPage وDialog يقبلان statusBar (وفي Rust .status_bar(StatusBarStyle::Light)، أو StatusBarSetting لبقية الحقول) مع foreground ("auto"، و"light" للخلفيات الداكنة، و"dark" للفاتحة) وhidden وanimated. تفوز الصفحة أو مربع الحوار الأحدث عرضًا، حقلًا بحقل، وحين تزول يُطبَّق ما تحتها من جديد. و"statusBar" في nsh.json هو الإعداد الافتراضي للتطبيق تحتها.

use nsh_base::nav_page;
use nsh_core::prelude::*;

fn photo_viewer() -> impl IntoElement {
    nav_page()
        .header_visible(false)
        .status_bar(StatusBarStyle::Light)
        .child(view().flex_1().bg(black()))
}

في تطبيق مدمج داخل تطبيق أصلي، يقرر متحكم العرض في التطبيق المضيف شريط الحالة على iOS. والتطبيق الذي يصرّح بـ "capabilities" في nsh.json يحتاج إلى "statusBar": true لتغييره.

#platform.os وplatform.select

يخبرك os() في Rust والثابت platform من nsh في TSX بمكان تشغيل التطبيق: Os::Ios أو Os::Android أو Os::MacOs أو Os::Windows أو Os::Linux أو Os::Web، وplatform.os "ios" أو "android" أو "macos" أو "windows" أو "linux" أو "web" (Mac Catalyst هو "ios"، مع platform.catalyst في أي من نمطيه؛ والواجهة الخلفية لـ macOS هي "macos"، والواجهة الخلفية لـ Linux هي "linux"، والواجهة الخلفية لـ Windows هي "windows"، وتطبّق عناصرها خصائص desktop؛ والمتصفحات هي "web"، وتطبّق خصائص desktop أيضًا عندما يكون platform.desktop هو true). انظر الجهاز والبيئة للإصدار والنمط وغيرهما.

use nsh_core::{element::Os, prelude::*};

if os() == Os::Android {
    // …
}

#platform.select

في Rust يختار match os() القيمة، مع is_desktop() لأجهزة سطح المكتب. وفي TSX تعيد platform.select(spec) القيمة الخاصة بهذه المنصة. وهي تبحث عن مفتاح نظام التشغيل نفسه، ثم desktop أو mobile وفق القاعدة أعلاه (platform.desktop)، ثم native (كل المنصات عدا الويب)، ثم default:

use nsh_core::{element::Os, prelude::*};

fn card() -> View {
    let card = view().rounded(px(12.0)).bg(white());
    match os() {
        Os::Ios => card.shadow_md(),
        Os::Android => card.border_1().border_color(rgb(0xe5e7eb)),
        _ => card,
    }
}

let hint = match os() {
    Os::Ios => "Swipe down to close",
    Os::Android => "Press back to close",
    _ => "Close",
};

وتعيد undefined عندما لا يتطابق شيء ولا يوجد default.

#عرض مكونات مختلفة

اختر المكونات بالطريقة نفسها، على مستوى الوحدة (TSX) عندما لا يتغير الاختيار أبدًا:

fn spinner() -> AnyElement {
    if os() == Os::Ios {
        ios_spinner().into_element()
    } else {
        material_spinner().into_element()
    }
}

أو ضمن الشيفرة مباشرة باستخدام when / <If>:

when(os() == Os::Ios, || input_accessory_view("bar").child(…))
    .otherwise(|| text("Android has no input accessories."))

#متغيرات المنصات في Tailwind

تنسّق Rust الأنماط لكل منصة باستخدام .when(os() == Os::Ios, |v| ..)؛ وفي TSX تطبّق المتغيرات ios: وandroid: وweb: الفئات على منصة واحدة:

view()
    .rounded_xl()
    .bg(white())
    .p_4()
    .when(os() == Os::Ios, |v| v.shadow_md())
    .when(os() == Os::Android, |v| v.border_1().border_color(rgb(0xe4e4e7)))

#فضّل السلوك المشترك

قبل اللجوء إلى فحص المنصة، فكّر فيما إذا كان الاختلاف يهم مستخدميك. فالعناصر والأنماط تُطابَق أصلًا مع السلوك الأصلي لكل منصة حيث ينبغي أن يختلف (فيزياء التمرير، وعرض النصوص، ولوحات المفاتيح، وإمكانية الوصول)، وتبقى متطابقة حيث يتوقع المستخدمون الاتساق (مظهر تطبيقك الخاص). وأفضل استخدام لفحوصات المنصة هو للأعراف التي يعتمد عليها المستخدمون: زر الرجوع في Android ورسائل toast، وأوراق iOS السفلية والعناوين الكبيرة، وأسماء الملء التلقائي، وتأثير ripple في Android.

#ذو صلة