شرح الكلاس ContextMenu في javafx

شرح الكلاس ContextMenu

الكلاس ContextMenu يستخدم للحصول على قائمة منبثقة ( Popup Menu ) تظهر عند النقر على زر الفأرة الأيمن ( Right Click ).

ملاحظة: ليس بالضرورة أن تظهر القائمة فقط عند النقر على زر الفأرة الأيمن, لكنك على الأغلب لن تحتاج إظهارها إلا عند النقر على زر الفأرة الأيمن.


في الـ ContextMenu يمكنك وضع أي نوع من العناصر ( Menu Items ) التالية بداخلها:

  • Menu: أي قائمة داخلية, حيث يمكنك أن تضع قائمة بداخل قائمة.

  • MenuItem: عنصر عادي خاص للقوائم.

  • RadioMenuItem: يمثل RadioButton خاص للقوائم.

  • CheckMenuItem: يمثل CheckBox خاص للقوائم.

  • SeparatorMenuItem: يمثل خط فاصل (Separator) خاص للقوائم.

  • CustomMenuItem: عبارة عن كلاس خاص يجب أن يرث منه أي كلاس تنشئه أنت بهدف وضعه كعنصر في القائمة.



مثال

الصورة التالية عبارة عن ContextMenu تحتوي على إثنين ContextMenu, إثنين RadioMenuItem, إثنين CheckMenuItem و إثنين SeparatorMenuItem.

شرح الكلاس  ContextMenu

لعلك تتسائل ما الفرق بين الـ RadioMenuItem و الـ CheckMenuItem لأنهما يعطيانك نفس الشكل!
الفرق بينهما أنك تستطيع وضع الـ RadioMenuItem ضمن مجموعة مثل الـ RadioButton تماماً لجعل المستخدم قادر على إختيار عنصر واحد فقط كما سبق أن شرحنا في درس الـ radiobutton.


بناء الكلاس ContextMenu

@IDProperty(value="id")
public class ContextMenu
extends PopupControl
	

كونستركتورات الكلاس ContextMenu

الجدول التالي يحتوي على كونستركتورات الكلاس ContextMenu.

الكونستركتور مع تعريفه
public ContextMenu() ينشئ كائن من الكلاس ContextMenu يمثل قائمة فارغة, أي لا تحتوي أي عنصر.
لإضافة خيارات فيها نستدعي الدالة getItems() التي تسمح للوصول لعناصرها و من ثم نستدعي الدالة add() أو addAll() لإضافة عناصر فيها كالتالي.

contextMenuObject.getItems().add( item );
contextMenuObject.getItems().addAll( item1, item2.. );

public ContextMenu(MenuItem... items) ينشئ كائن من الكلاس ContextMenu يمثل قائمة تحتوي على عناصر.
مكان الباراميتر items يمكنك أن تمرر العدد الذي تريده من الكائنات التي ترث من الكلاس MenuItem مع وضع فاصلة بين كل إثنين. هذه العناصر ستظهر كعناصر في القائمة.

دوال الكلاس ContextMenu

الجدول التالي يحتوي على دوال الكلاس ContextMenu الأكثر إستخداماً.

الدالة مع تعريفها
public void show(Node anchor, double screenX, double screenY) تستخدم لإظهار القائمة التي يمثلها كائن الـ ContextMenu الذي قام باستدعائها في الموقع الذي تم تحديده.

  • مكان الباراميتر anchor نضع إسم الكائن الذي سنظهر القائمة من أجله, و الذي أعددنا القائمة له من الأساس.

  • مكان الباراميتر screenX نضع رقم بالبيكسل يمثل بعد القائمة أفقياً عن المكان الذي تم النقر عليه.

  • مكان الباراميتر screenY نضع رقم بالبيكسل يمثل بعد القائمة عامودياً عن المكان الذي تم النقر عليه.

public final void setStyle(String value) تستخدم لتعديل تصميم كائن الـ ContextMenu الذي قام بإستدعائها.
مكان الباراميتر value يمكنك تمرير إسم و قيمة أي خاصية تريد تعديلها في كائن الـ ContextMenu بأسلوب لغة CSS لإظهاره بالشكل الذي تريده.

مثال طريقة إظهار قائمة ContextMenu عند النقر على زر الفأرة الأيمن في javafx

المثال التالي يعلمك طريقة إنشاء كائن من الكلاس ContextMenu يمثل قائمة تظهر عند النقر على زر الفأرة الأيمن ( Right Click ).
هذه القائمة تسمح للمستخدم بقلب الصورة يميناً و يساراً, إظهار خط حولها, تحديد لون الخط, إخفاء الخط.

ملاحظة: عليك إنشاء مجلد و وضع الصورة فيه كما في الصورة التالية.


الصورة توضح أننا أضفنا مجلد إسمه images و وضعنا فيه صورة واحدة إسمها javafx-icon.png.


مثال طريقة إنشاء كائن من الكلاس ContextMenu يمثل قائمة تظهر عند النقر على زر الفأرة الأيمن Right Click ).

Main.java
      import javafx.application.Application;
      import javafx.beans.value.ObservableValue;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.CheckMenuItem;
      import javafx.scene.control.ContextMenu;
      import javafx.scene.control.MenuItem;
      import javafx.scene.control.RadioMenuItem;
      import javafx.scene.control.SeparatorMenuItem;
      import javafx.scene.control.Toggle;
      import javafx.scene.control.ToggleGroup;
      import javafx.scene.image.Image;
      import javafx.scene.image.ImageView;
      import javafx.scene.input.MouseButton;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.layout.Pane;
      import javafx.stage.Stage;

      public class Main extends Application {

      // لأننا ننوي وضع الصورة بداخله pane هنا قمنا بإنشاء كائن من الكلاس
      Pane pane = new Pane();

      // يمثل الصورة التي تظهر في النافذة Image هنا قمنا بإنشاء كائن من الكلاس
      Image image = new Image(getClass().getResourceAsStream("/images/javafx-icon.png"));

      // في النافذة image لعرض الصورة التي يشير لها الكائن ImageView هنا قمنا بإنشاء كائن من الكلاس
      ImageView imageView = new ImageView(image);

      // يمثل القائمة التي تظهر عن النقر بزر الفأرة الأيمن فوق الصورة ContextMenu هنا قمنا بإنشاء كائن من الكلاس
      ContextMenu contextMenu = new ContextMenu();

      // هنا قمنا بإنشاء جميع الكائنات التي سنستخدمها كعناصر بداخل القائمة
      MenuItem menuItem1 = new MenuItem("Rotate Right");
      MenuItem menuItem2 = new MenuItem("Rotate Left");
      CheckMenuItem checkMenuItem = new CheckMenuItem("Show Border");
      RadioMenuItem radioMenuItem1 = new RadioMenuItem("Black Border");
      RadioMenuItem radioMenuItem2 = new RadioMenuItem("Blue Border");

      // radioMenuItem2 و radioMenuItem1 لأننا ننوي وضع الكائنين toggleGroup هنا قمنا بإنشاء كائن من الكلاس
      // لاحقاً في مجموعة واحدة لجعل المستخدم قادر على إختيار إحداهما في كل مرة و ليس كلاهما 
      ToggleGroup toggleGroup = new ToggleGroup();


      // الذي يحتوي على الصورة pane هنا قمنا بتعريف دالة خاصة لرسم أو إخفاء حدود الكائن
      public void drawBorderOnDemande()
      {
      // checkMenuItem إذا كان يوجد على صح على الكائن
      if (checkMenuItem.isSelected())
      {
      // pane سيتم إظهار حدود لونها أزرق للكائن radioMenuItem1 إذا كان يوجد علامة صح على الكائن
      if (radioMenuItem1.isSelected())
      {
      pane.setStyle("-fx-border-style: solid; -fx-border-color: black;");
      }
      // pane سيتم إظهار حدود لونها أسود للكائن radioMenuItem2 إذا كان يوجد علامة صح على الكائن
      else
      {
      pane.setStyle("-fx-border-style: solid; -fx-border-color: blue;");
      }
      }
      // pane سيتم إزالة الحدود عن الكائن checkMenuItem إذا لم يكن هناك علامة صح على الكائن
      else
      {
      pane.setStyle("-fx-border:none;");
      }
      }


      // قابلين للنقر أم لا radioMenuItem2 و radioMenuItem1 هنا قمنا بتعريف دالة خاصة لتحديد ما إذا كان يجب جعل الكائنين
      public void autoEnableOrDisableMenuOptions()
      {
      // قابلين للنقر radioMenuItem2 و radioMenuItem1 سيتم جعل الكائنين checkMenuItem إذا قام المستخدم بوضع علامة صح على الكائن
      if (checkMenuItem.isSelected())
      {
      radioMenuItem1.setDisable(false);
      radioMenuItem2.setDisable(false);
      }
      // غير قابلين للنقر radioMenuItem2 و radioMenuItem1 سيتم جعل الكائنين checkMenuItem إذا قام المستخدم بإزالة علامة الصح عن الكائن
      else
      {
      radioMenuItem1.setDisable(true);
      radioMenuItem2.setDisable(true);
      }
      }


      @Override
      public void start(Stage stage) {

      // في النافذة pane هنا قمنا بتحديد حجم و مكان الكائن
      pane.setPrefSize(202, 202);
      pane.setTranslateX(99);
      pane.setTranslateY(24);

      // pane في الكائن imageView هنا قمنا بوضع الكائن
      pane.getChildren().add(imageView);

      // للأننا وضعناه بداخله pane بالنسبة للكائن imageView هنا قمنا بتحديد حجم و مكان الكائن
      imageView.setFitWidth(200);
      imageView.setFitHeight(200);
      imageView.setTranslateX(1);
      imageView.setTranslateY(1);

      // و هكذا أصبح يمكن إختيار واحد منهم فقط في كل مرة radioMenuItem2 و radioMenuItem1 و ربطنا فيه الكائنين ToggleGroup هنا قمنا بإنشاء كائن من الكلاس
      radioMenuItem1.setToggleGroup(toggleGroup);
      radioMenuItem2.setToggleGroup(toggleGroup);

      // هنا قمنا بوضع كل جميع الكائنات التي أنشأناها خصيصاً لوضعها كعناصر في القائمة بداخلها
      contextMenu.getItems().add(menuItem1);
      contextMenu.getItems().add(menuItem2);
      contextMenu.getItems().add(new SeparatorMenuItem());
      contextMenu.getItems().add(checkMenuItem);
      contextMenu.getItems().add(new SeparatorMenuItem());
      contextMenu.getItems().add(radioMenuItem1);
      contextMenu.getItems().add(radioMenuItem2);

      // في النافذة Root Node لأننا ننوي جعله الـ Group هنا قمنا بإنشاء كائن من الكلاس
      Group root = new Group();

      // root في الكائن datePicker هنا قمنا بإضافة الكائن
      root.getChildren().add(pane);

      // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
      Scene scene = new Scene(root, 400, 250);

      // هنا وضعنا عنوان للنافذة
      stage.setTitle("JavaFX ContextMenu");

      // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
      stage.setScene(scene);

      // هنا قمنا بإظهار النافذة
      stage.show();

      // الذي يمثل الصورة imageView هنا قمنا بتحديد ماذا سيحدث عند النقر بواسطة الفأرة على الكائن
      imageView.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent e) -> {
      // في المكان الذي تم النقر عليه (contextMenu) إذا تم النقر على الصورة من الفأرة بواسطة الزر الأيمن, سيتم إظهار القائمة
      if (e.getButton() == MouseButton.SECONDARY) {
      contextMenu.show(imageView, e.getScreenX(), e.getScreenY());
      }
      });

      // menuItem1 هنا قمنا بتحديد ماذا سيحدث عند النقر على الخيار الذي يمثل الكائن
      menuItem1.setOnAction((ActionEvent) -> {
      // سيتم جعل الصورة تدور 90 درجة باتجاه اليمين
      imageView.setRotate(imageView.getRotate() + 90);
      });

      // menuItem2 هنا قمنا بتحديد ماذا سيحدث عند النقر على الخيار الذي يمثل الكائن
      menuItem2.setOnAction((ActionEvent) -> {
      // سيتم جعل الصورة تدور 90 درجة باتجاه اليسار
      imageView.setRotate(imageView.getRotate() - 90);
      });

      // checkMenuItem هنا قمنا بتحديد ماذا سيحدث عند النقر على الخيار الذي يمثل الكائن
      checkMenuItem.setOnAction((ActionEvent) -> {
      // لتحديد ما إذا كان يجب رسم حدود حول الصورة أم لا autoEnableOrDisableMenuOptions() و drawBorderOnDemande() سيتم إستدعاء الدالتين
      // أم لا radioMenuItem2 و radioMenuItem1 و كيف يجب أن يتم تلوين هذه الحدود و لتحديد ما إذا كان يمكن النقر على الكائنين
      drawBorderOnDemande();
      autoEnableOrDisableMenuOptions();
      });

      // group تابع للمجموعة RadioButton هنا قمنا بتحديد ماذا سيحدث عند النقر على أي
      toggleGroup.selectedToggleProperty().addListener((ObservableValue<? extends Toggle> ov, Toggle toggle, Toggle new_toggle) -> {
      drawBorderOnDemande();
      });

      // غير مختار عند تشغيل التطبيق, أي لا يوجد عليه علامة صح checkMenuItem هنا قمنا بجعل الكائن
      checkMenuItem.setSelected(false);

      // مختاراً بشكل إفتراضي عند تشغيل التطبيق, أي يوجد عليه علامة صح radioMenuItem1 هنا قمنا بجعل الكائن
      radioMenuItem1.setSelected(true);

      // أم لا radioMenuItem2 و radioMenuItem1 لتحديد ما إذا كان يمكن النقر على الكائنين autoEnableOrDisableMenuOptions() هنا قمنا باستدعاء الدالة
      autoEnableOrDisableMenuOptions();

      }

      // هنا قمنا بتشغيل التطبيق
      public static void main(String[] args) {
      launch(args);
      }

      }
    

ستظهر لك النافذة التالية عند التشغيل.

طريقة إظهار قائمة ContextMenu عند النقر على زر الفأرة الأيمن في javafx

كورس شرح  javaFX