# ComboBox (JavaScript)

Taryn Fox
jewelfox@fursona.net 2012
Ένα προσαρμόσιμο πτυσσόμενο μενού
Δημήτρης Σπίγγος
dmtrs32@gmail.com 2012, 2013

# ComboBox

Ένα ComboBox είναι ένα άκρως προσαρμόσιμο πτυσσόμενο μενού. Κρατά το ισοδύναμο ενός γραφικού συστατικού TreeView που εμφανίζεται όταν το πατάτε πάνω του, πλήρες με μια αποθήκη λιστών (ListStore) (βασικά ένα υπολογιστικό φύλλο) που λέει τι είναι στις γραμμές και στις στήλες. Σε αυτό το παράδειγμα, η αποθήκη λιστών μας έχει το όνομα κάθε επιλογής σε μια στήλη και το όνομα ενός εικονιδίου παρακαταθήκης στην άλλη, που το σύνθετο πλαίσιο έπειτα μετατρέπει σε ένα εικονίδιο για κάθε επιλογή.

Επιλέγετε μια πλήρως οριζόντια γραμμή τη φορά, έτσι τα εικονίδια δεν συμπεριφέρονται ως ξεχωριστές επιλογές. Αυτές και το κείμενο δίπλα τους κατασκευάζουν κάθε επιλογή που μπορείτε να πατήσετε.

Η εργασία με μια αποθήκη λιστών μπορεί να είναι χρονοβόρα. Εάν θέλετε μόνο ένα απλό πτυσσόμενο μενού μόνο για κείμενο, ρίξτε μια ματιά στο ComboBoxText. Δεν παίρνει πολύ χρόνο για εγκατάσταση και δουλεύετε πιο εύκολα.

# Βιβλιοθήκες για εισαγωγή

#!/usr/bin/gjs

const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang;

Αυτές είναι οι βιβλιοθήκες που χρειαζόμαστε να εισάγουμε αυτήν την εφαρμογή για να εκτελεστεί. Να θυμόσαστε ότι η γραμμή που λέει στο GNOME ότι χρησιμοποιούμε Gjs χρειάζεται πάντοτε να πάει στην αρχή.

# Δημιουργία του παραθύρου εφαρμογής

const ComboBoxExample = new Lang.Class ({ Name: 'ComboBox Example',

// Δημιουργία της εφαρμογής αυτής καθεαυτής _init: function () { this.application = new Gtk.Application ({ application_id: 'org.example.jscombobox'});

// Σύνδεση των σημάτων 'activate' και 'startup' στις συναρτήσεις επανάκλησης this.application.connect('activate', Lang.bind(this, this._onActivate)); this.application.connect('startup', Lang.bind(this, this._onStartup)); },

// Η συνάρτηση επανάκλησης για το σήμα 'activate' παρουσιάζει παράθυρα όταν είναι ενεργή _onActivate: function () { this._window.present (); },

// Η συνάρτηση επανάκλησης για το σήμα 'startup' δομεί τη διεπαφή χρήστη _onStartup: function () { this._buildUI (); },

Όλος ο κώδικας για αυτό το παράδειγμα πηγαίνει στην κλάση ComboBoxExample. Ο παραπάνω κώδικας δημιουργεί μια Gtk.Application για να μπουν μέσα τα γραφικά συστατικά μας και τα παράθυρα.

// Δόμηση της διεπαφής χρήστη της εφαρμογής _buildUI: function () {

// Δημιουργία του παραθυύρου της εφαρμογής this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Welcome to GNOME", default_width: 200, border_width: 10 });

Η συνάρτηση _buildUI είναι εκεί που βάζουμε όλον τον κώδικα για να δημιουργήσουμε τη διεπαφή χρήστη της εφαρμογής. Το πρώτο βήμα δημιουργεί ένα νέο Gtk.ApplicationWindow για να βάλουμε μέσα τα γραφικά συστατικά μας.

# Δημιουργία της αποθήκης λιστών (ListStore)

// Δημιουργία της αποθήκης λιστών για να τοποθετηθούν μέσα οι επιλογές μας this._listStore = new Gtk.ListStore(); this._listStore.set_column_types ([ GObject.TYPE_STRING, GObject.TYPE_STRING]);

Αυτή η αποθήκη λιστών (ListStore) δουλεύει όπως αυτή που χρησιμοποιήθηκε στο παράδειγμα TreeView. Του δίνουμε δύο στήλες, αμφότερες συμβολοσειρές, επειδή η μια τους θα περιέχει τα ονόματα των εικονιδίων παρακαταθήκης Gtk.

Εάν θα θέλαμε να χρησιμοποιήσουμε τα δικά μας εικονίδια που δεν ήταν ήδη ενσωματωμένα στο GNOME, θα χρειαζόμασταν να χρησιμοποιήσουμε τον τύπο gtk.gdk.Pixbuf. Να μερικοί άλλοι τύποι που μπορείτε να χρησιμοποιήσετε:

GObject.TYPE_BOOLEAN -- αληθές ή ψευδές

GObject.TYPE_FLOAT -- Ένας αριθμός κινητής υποδιαστολής (ένας με δεκαδική υποδιαστολή)

GObject.TYPE_STRING -- Μια συμβολοσειρά γραμμάτων και αριθμών

Χρειάζεται να βάλετε τη γραμμή const GObject = imports.gi.GObject; στην αρχή του κώδικα της εφαρμογή σας, όπως κάναμε σε αυτό το παράδειγμα, εάν θέλετε να μπορείτε να χρησιμοποιήσετε τύπους GObject.

// Αυτός ο πίνακας κρατά τη λίστα των επιλογών μας και των εικονιδίων τους let options = [{ name: "Select" }, { name: "New", icon: Gtk.STOCK_NEW }, { name: "Open", icon: Gtk.STOCK_OPEN }, { name: "Save", icon: Gtk.STOCK_SAVE }];

// Τοποθέτηση των επιλογών στην αποθήκη λιστών for (let i = 0; i < options.length; i++ ) { let option = options[i]; let iter = this._listStore.append(); this._listStore.set (iter, [0], [option.name]); if ('icon' in option) this._listStore.set (iter, [1], [option.icon]); }

Εδώ δημιουργούμε έναν πίνακα των επιλογών κειμένου και τα αντίστοιχα τους εικονίδια, έπειτα τα βάζουμε στην αποθήκη λιστών (ListStore) με τον ίδιο χοντρικά τρόπο που θα κάναμε για μία αποθήκη λιστών της προβολής δένδρου. Θέλουμε μόνο να βάλουμε ένα εικονίδιο εάν υπάρχει πράγματι ένα εικονίδιο στον πίνακα επιλογών, έτσι βεβαιωνόμαστε για να ελεγχθεί αυτό πρώτο.

Η "Select" δεν είναι πραγματικά μια επιλογή όσο μια πρόσκληση να πατήσουμε στο ComboBox μας, έτσι δεν χρειάζεται ένα εικονίδιο.

# Δημιουργία των ComboBox

// Δημιουργία του σύνθετου πλαισίου this._comboBox = new Gtk.ComboBox({ model: this._listStore});

Κάθε σύνθετο πλαίσιο έχει ένα υποκείμενο "model" από όπου παίρνει όλες τις επιλογές του. Μπορείτε να χρησιμοποιήσετε μια αποθήκη δένδρου εάν θέλετε να έχετε ένα σύνθετο πλαίσιο με επιλογές διακλάδωσης. Σε αυτήν την περίπτωση, χρησιμοποιούμε απλά τη αποθήκη λιστών που ήδη δημιουργήσαμε.

// Δημιουργία μερικών απεικονιστών κελιού για τα στοιχεία σε κάθε στήλη let rendererPixbuf = new Gtk.CellRendererPixbuf(); let rendererText = new Gtk.CellRendererText();

// Συσκευασία των απεικονιστών στο σύνθετο πλαίσιο με τη σειρά που θέλουμε να δούμε this._comboBox.pack_start (rendererPixbuf, false); this._comboBox.pack_start (rendererText, false);

// Ορισμός των απεικονιστών για να χρησιμοποιήσουν τις πληροφορίες από την αποθήκη λιστών μας this._comboBox.add_attribute (rendererText, "text", 0); this._comboBox.add_attribute (rendererPixbuf, "stock_id", 1);

Αυτό το μέρος, πάλι, δουλεύει παρόμοια με τη δημιουργία απεικονιστών κελιών (CellRenderers) και πακετάρισμα τους στις στήλες μιας TreeView. Η μεγαλύτερη διαφορά είναι ότι δεν χρειαζόμαστε να δημιουργήσουμε τις στήλες του σύνθετου πλαισίου (ComboBox) ως ξεχωριστά αντικείμενα. Απλά πακετάρουμε τους απεικονιστές κελιών σε αυτό με τη σειρά που τα θέλουμε να εμφανιστούν, έπειτα τους λέμε να πάρουν πληροφορίες από τη αποθήκη λιστών (ListStore) (και τι τύπο πληροφορίας θέλουμε να τους περιμένει).

Χρησιμοποιούμε ένα κείμενο απεικονιστή κελιού (CellRendererText) για να εμφανίσουμε το κείμενο και έναν απεικονιστή κελιού Pixbuf (CellRendererPixbuf) για να προβάλουμε τα εικονίδια. Μπορούμε να αποθηκεύσουμε τα ονόματα τύπων παρακαταθήκης των εικονιδίων ως συμβολοσειρές, αλλά όταν τα εμφανίζουμε χρειαζόμαστε έναν απεικονιστή κελιού (CellRenderer) που σχεδιάστηκε για εικόνες.

Ακριβώς όπως με μια προβολή δένδρου (TreeView), το "model" (σε αυτήν την περίπτωση μια αποθήκη λιστών (ListStore)) και η "view" (σε αυτήν την περίπτωση το σύνθετο πλαίσιο (ComboBox) μας) είναι ξεχωριστά. Λόγω αυτού, μπορούμε να κάνουμε πράγματα όπως να έχουμε τις στήλες σε μια σειρά στην αποθήκη λιστών και έπειτα πακετάρουμε τους απεικονιστές κελιών (CellRenderers) που αντιστοιχούν σε αυτές τις στήλες στο σύνθετο πλαίσιο με διαφορετική σειρά. Μπορούμε ακόμα να δημιουργήσουμε μια προβολή δένδρου ή άλλο γραφικό συστατικό που εμφανίζει την πληροφορία στην αποθήκη λίστας με διαφορετικό τρόπο, χωρίς αυτό να επηρεάζει το σύνθετο πλαίσιο.

// Ορισμός της πρώτης γραμμής στο σύνθετο πλαίσιο να είναι ενεργή στην έναρξη this._comboBox.set_active (0);

// Σύνδεση του σήματος του σύνθετου πλαισίου 'changed' στη συνάρτηση επανάκλησής μας this._comboBox.connect ('changed', Lang.bind (this, this._onComboChanged));

Θέλουμε το κείμενο "Select" να είναι το τμήμα που βλέπεται πρώτα, αυτό που τα δέχεται στο πάτημα του ComboBox. Έτσι το ορίζουμε να είναι η ενεργή καταχώριση. Επίσης συνδέουμε το σήμα changed του ComboBox σε μια συνάρτηση επανάκλησης, έτσι ώστε οποτεδήποτε κάποιος πατά σε μια νέα επιλογή κάτι να συμβαίνει. Σε αυτήν την περίπτωση, πρόκειται απλά να εμφανίσουμε ένα αναδυόμενο με ένα μικρό ποίημα.

// Προσθήκη του σύνθετου πλαισίου στο παράθυρο this._window.add (this._comboBox);

// Εμφάνιση του παραθύρου και όλων των θυγατρικών γραφικών συστατικών this._window.show_all(); },

Τελικά, προσθέτουμε το ComboBox στο παράθυρο, έπειτα λέμε στο παράθυρο να εμφανίσει τον εαυτόν του και όλα μέσα του.

# Η συνάρτηση που χειρίζεται την επιλογή σας

_selected: function () {

// Το χαζό ψευδοποίημα που θα χρησιμοποιήσουμε για τον διάλογο μηνύματός μας let haiku = ["", "You ask for the new\nwith no thought for the aged\nlike fallen leaves trod.", "Like a simple clam\nrevealing a lustrous pearl\nit opens for you.", "A moment in time\na memory on the breeze\nthese things can't be saved."];

Πρόκειται να δημιουργήσουμε ένα αναδυόμενο MessageDialog, που σας εμφανίζει ένα χαζό ποίημα με βάση ποια διανομή επιλέγετε. Πρώτα, δημιουργούμε τον πίνακα του ποιήματος για χρήση. Αφού η πρώτη συμβολοσειρά στο ComboBox μας είναι μόνο το μήνυμα "Select", κάνουμε την πρώτη συμβολοσειρά στον πίνακά μας κενή.

// Ποιο στοιχείο του πλαισίου ελέγχου είναι ενεργό; let activeItem = this._comboBox.get_active();

// Κανένας διάλογος μηνύματος αν επιλέξετε "Select" if (activeItem != 0) { this._popUp = new Gtk.MessageDialog ({ transient_for: this._window, modal: true, buttons: Gtk.ButtonsType.OK, message_type: Gtk.MessageType.INFO, text: haiku[activeItem]});

// Σύνδεση του κουμπιού ΕΝΤΑΞΕΙ σε μια συνάρτηση χειρισμού this._popUp.connect ('response', Lang.bind (this, this._onDialogResponse));

// Εμφάνιση του διαλόγου μηνύματος this._popUp.show(); }

},

Πριν την προβολή ενός MessageDialog, δοκιμάζουμε πρώτα για να βεβαιωθούμε ότι δεν επιλέξατε το μήνυμα "Select". Μετά από αυτό, ορίζουμε το κείμενο του να είναι στο ποίημα στον πίνακα που αντιστοιχεί στην ενεργή καταχώριση στο ComboBoxText μας. Το κάνουμε χρησιμοποιώντας τη μέθοδο get_active, που επιστρέφει τον αριθμό ταυτότητας της επιλογής σας.

Άλλες μέθοδοι που μπορείτε να χρησιμοποιήσετε περιλαμβάνουν get_active_id, που επιστρέφει το αναγνωριστικό κειμένου που αποδόθηκε από append και get_active_text, που επιστρέφει το πλήρες κείμενο της επιλεγμένης συμβολοσειράς.

Μετά τη δημιουργία του MessageDialog, συνδέουμε το σήμα της απάντησής του στη συνάρτηση _onDialogResponse, έπειτα του λέμε να εμφανίσει τον εαυτόν του.

_onDialogResponse: function () {

this._popUp.destroy ();

}

});

Αφού το μόνο κουμπί που ο MessageDialog έχει είναι ένα κουμπί εντάξει, δεν χρειαζόμαστε να ελέγξουμε το response_id του για να δούμε ποιο κουμπί πατήθηκε. Αυτό που κάνουμε εδώ είναι η καταστροφή του αναδυόμενου.

// Εκτέλεση της εφαρμογής let app = new ComboBoxExample (); app.application.run (ARGV);

Τελικά, δημιουργούμε ένα νέο στιγμιότυπο της ολοκληρωμένης κλάσης ComboBoxExample και εκτελούμε την εφαρμογή.

# Δείγμα πλήρους κώδικα

#!/usr/bin/gjs

const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang;

const ComboBoxExample = new Lang.Class ({ Name: 'ComboBox Example',

// Δημιουργία της εφαρμογής αυτής καθεαυτής _init: function () { this.application = new Gtk.Application ({ application_id: 'org.example.jscombobox'});

// Σύνδεση των σημάτων 'activate' και 'startup' στις συναρτήσεις επανάκλησης this.application.connect('activate', Lang.bind(this, this._onActivate)); this.application.connect('startup', Lang.bind(this, this._onStartup)); },

// Η συνάρτηση επανάκλησης για το σήμα 'activate' παρουσιάζει παράθυρα όταν είναι ενεργή _onActivate: function () { this._window.present (); },

// Η συνάρτηση επανάκλησης για το σήμα 'startup' δομεί τη διεπαφή χρήστη _onStartup: function () { this._buildUI (); },

// Δόμηση της διεπαφής χρήστη της εφαρμογής _buildUI: function () {

// Δημιουργία του παραθύρου της εφαρμογής this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Welcome to GNOME", default_width: 200, border_width: 10 });

// Δημιουργία της αποθήκης λιστών για να τοποθετηθούν μέσα οι επιλογές μας this._listStore = new Gtk.ListStore(); this._listStore.set_column_types ([ GObject.TYPE_STRING, GObject.TYPE_STRING]);

// Αυτός ο πίνακας κρατά τη λίστα των επιλογών μας και τα εικονίδιά τους let options = [{ name: "Select" }, { name: "New", icon: Gtk.STOCK_NEW }, { name: "Open", icon: Gtk.STOCK_OPEN }, { name: "Save", icon: Gtk.STOCK_SAVE }];

// Τοποθέτηση των επιλογών στη αποθήκη λιστών for (let i = 0; i < options.length; i++ ) { let option = options[i]; let iter = this._listStore.append(); this._listStore.set (iter, [0], [option.name]); if ('icon' in option) this._listStore.set (iter, [1], [option.icon]); }

// Δημιουργία του σύνθετου πλαισίου this._comboBox = new Gtk.ComboBox({ model: this._listStore});

// Δημιουργία μερικών απεικονιστών κελιών για τα στοιχεία σε κάθε στήλη let rendererPixbuf = new Gtk.CellRendererPixbuf(); let rendererText = new Gtk.CellRendererText();

// Συσκευασία των απεικονιστών στο σύνθετο πλαίσιο με την επιθυμητή σειρά this._comboBox.pack_start (rendererPixbuf, false); this._comboBox.pack_start (rendererText, false);

// Ορισμός των απεικονιστών για να χρησιμοποιήσουν τις πληροφορίες από την αποθήκη των λιστών μας this._comboBox.add_attribute (rendererText, "text", 0); this._comboBox.add_attribute (rendererPixbuf, "stock_id", 1);

// Ορισμός της πρώτης γραμμής στο σύνθετο πλαίσιο να είναι ενεργή στην έναρξη this._comboBox.set_active (0);

// Σύνδεση του σήματος του σύνθετου πλαισίου 'changed' στη συνάρτηση επανάκλησής μας this._comboBox.connect ('changed', Lang.bind (this, this._onComboChanged));

// Προσθήκη του σύνθετου πλαισίου στο παράθυρο this._window.add (this._comboBox);

// Εμφάνιση του παραθύρου και όλων των θυγατρικών γραφικών συστατικών this._window.show_all(); },

_onComboChanged: function () {

// Το χαζό ψευδοποίημα που θα χρησιμοποιήσουμε για τον διάλογο μηνυμάτων μας let haiku = ["", "You ask for the new\nwith no thought for the aged\nlike fallen leaves trod.", "Like a simple clam\nrevealing a lustrous pearl\nit opens for you.", "A moment in time\na memory on the breeze\nthese things can't be saved."];

// Ποιο στοιχείο του σύνθετου πλαισίου είναι ενεργό; let activeItem = this._comboBox.get_active();

// Κανένας διάλογος μηνύματος αν επιλέξετε "Select" if (activeItem != 0) { this._popUp = new Gtk.MessageDialog ({ transient_for: this._window, modal: true, buttons: Gtk.ButtonsType.OK, message_type: Gtk.MessageType.INFO, text: haiku[activeItem]});

// Σύνδεση του κουμπιού ΕΝΤΑΞΕΙ σε συνάρτηση χειρισμού this._popUp.connect ('response', Lang.bind (this, this._onDialogResponse));

// Εμφάνιση του διαλόγου μηνύματος this._popUp.show(); }

},

_onDialogResponse: function () {

this._popUp.destroy ();

}

});

// Εκτέλεση της εφαρμογής let app = new ComboBoxExample (); app.application.run (ARGV);

# Τεκμηρίωση σε βάθος

Σε αυτό το παράδειγμα χρησιμοποιήσαμε τα παρακάτω:

Gtk.Application

Gtk.ApplicationWindow

Gtk.CellRendererPixbuf

Gtk.CellRendererText

Gtk.ComboBox

Gtk.ListStore

Gtk.MessageDialog

Gtk.TreeIter