/*
* ArcMenu - A traditional application menu for GNOME 3
*
* ArcMenu Lead Developer and Maintainer
* Andrew Zaech https://gitlab.com/AndrewZaech
*
* ArcMenu Founder, Former Maintainer, and Former Graphic Designer
* LinxGem33 https://gitlab.com/LinxGem33 - (No Longer Active)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
const Me = imports.misc.extensionUtils.getCurrentExtension();
const {Gdk, GdkPixbuf, Gio, GLib, GObject, Gtk} = imports.gi;
const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
const _ = Gettext.gettext;
var Notebook = GObject.registerClass(class Arc_Menu_Notebook extends Gtk.Notebook{
_init() {
super._init({
margin_start: 0,
margin_end: 0
});
}
append_page(notebookPage) {
Gtk.Notebook.prototype.append_page.call(
this,
notebookPage,
notebookPage.getTitleLabel()
);
}
});
var NotebookPage = GObject.registerClass(class Arc_Menu_NotebookPage extends Gtk.Box {
_init(title) {
super._init({
orientation: Gtk.Orientation.VERTICAL,
margin_top: 24,
margin_bottom: 24,
margin_start: 24,
margin_end: 24,
spacing: 20,
homogeneous: false
});
this._title = new Gtk.Label({
label: "" + title + "",
use_markup: true,
xalign: 0
});
}
getTitleLabel() {
return this._title;
}
});
var Button = GObject.registerClass(class Arc_Menu_Button extends Gtk.Button {
_init(params) {
super._init();
this._params = params;
this.halign = Gtk.Align.END;
this.valign = Gtk.Align.CENTER;
this.box = new Gtk.Box({
orientation: Gtk.Orientation.HORIZONTAL,
spacing: 5
});
this.set_child(this.box);
if (this._params.icon_name) {
let image = new Gtk.Image({
icon_name: this._params.icon_name,
halign: Gtk.Align.CENTER
});
this.box.append(image);
}
if (this._params.tooltip_text){
this.set_tooltip_text(this._params.tooltip_text);
}
if (this._params.title){
let label = new Gtk.Label({
label: _(this._params.title),
use_markup: true,
xalign: 0
});
if(this._params.icon_first)
this.box.append(label);
else
this.box.prepend(label);
}
}
});
var DialogWindow = GObject.registerClass(class Arc_Menu_DialogWindow extends Gtk.Dialog {
_init(title, parent) {
super._init({
title: title,
transient_for: parent.get_root(),
modal: true
});
let vbox = new Gtk.Box({
orientation: Gtk.Orientation.VERTICAL,
spacing: 20,
homogeneous: false,
margin_top: 5,
margin_bottom: 5,
margin_start: 5,
margin_end: 5,
hexpand: true,
halign: Gtk.Align.FILL
});
vbox.insert_after(this.get_content_area(), null);
this._createLayout(vbox);
}
_createLayout(vbox) {
throw "Not implemented!";
}
});
var MessageDialog = GObject.registerClass(class Arc_Menu_MessageDialog extends Gtk.MessageDialog {
_init(params) {
super._init({
transient_for: params.transient_for,
modal: true,
buttons: params.buttons
});
this.set_size_request(300, 50);
this.grid = new Gtk.Grid({
row_spacing: 10,
column_spacing: 24,
margin_top: 24,
margin_bottom: 0,
margin_start: 24,
margin_end: 24,
hexpand: false,
halign: Gtk.Align.CENTER
});
this.grid.insert_after(this.get_content_area(), null);
let text = new Gtk.Label({
label: "" + _(params.text) + "",
use_markup: true,
hexpand: false,
halign: Gtk.Align.START,
wrap: true,
});
this.grid.attach(text, 1, 0, 1, 1);
if(params.secondaryText){
let secondayText = new Gtk.Label({
label: _(params.secondaryText),
use_markup: true,
hexpand: false,
halign: Gtk.Align.START,
wrap: true,
});
this.grid.attach(secondayText, 1, 1, 1, 1);
}
if(params.iconName){
let image = new Gtk.Image({
icon_name: params.iconName,
pixel_size: 48
});
this.grid.attach(image, 0, 0, 1, 2);
}
}
});
var FrameBox = GObject.registerClass(class Arc_Menu_FrameBox extends Gtk.Frame {
_init(params) {
super._init(params);
this._listBox = new Gtk.ListBox();
this._listBox.set_selection_mode(Gtk.SelectionMode.NONE);
this.count = 0;
this.children = [];
Gtk.Frame.prototype.set_child.call(this, this._listBox);
}
add(boxRow) {
this._listBox.append(boxRow);
this.children.push(boxRow);
this.count++;
}
show() {
this._listBox.show();
super.show();
}
length() {
return this._listBox.length;
}
remove(boxRow){
if(boxRow){
this._listBox.remove(boxRow);
this.children = this.children.filter(e => e !== boxRow)
this.count = this.count -1;
}
}
removeChildrenAfterIndex(index){
let childrenCount = this.count;
for(let i = childrenCount - 1; i > index; i--){
let child = this._listBox.get_row_at_index(i);
if(child) this.remove(child);
}
this._listBox.show();
}
remove_all_children() {
for(let i = 0; i < this.children.length; i++){
let child = this.children[i];
this._listBox.remove(child);
}
this.children = [];
this.count = 0;
this._listBox.show();
}
get_index(index){
return this._listBox.get_row_at_index(index);
}
insert(row, pos){
this._listBox.insert(row, pos);
this.children.push(row);
this.count++;
}
});
var FrameBoxRow = GObject.registerClass(class Arc_Menu_FrameBoxRow extends Gtk.ListBoxRow {
_init(params) {
super._init(params);
this.selectable = false;
this.activatable = false;
this._grid = new Gtk.Grid({
orientation: Gtk.Orientation.HORIZONTAL,
margin_top: 5,
margin_bottom: 5,
margin_start: 5,
margin_end: 5,
column_spacing: 20,
row_spacing: 20
});
this.x = 0;
Gtk.ListBoxRow.prototype.set_child.call(this, this._grid);
}
add(widget) {
this._grid.attach(widget, this.x, 0, 1, 1);
this.x++;
}
setVerticalAlignmentBottom(){
this._grid.vexpand = true;
this._grid.valign = Gtk.Align.END;
}
});
var FrameBoxDragRow = GObject.registerClass(class Arc_Menu_FrameBoxDragRow extends Gtk.ListBoxRow {
_init(scrolledWindow) {
super._init();
this._grid = new Gtk.Grid({
orientation: Gtk.Orientation.HORIZONTAL,
margin_top: 5,
margin_bottom: 5,
margin_start: 5,
margin_end: 5,
column_spacing: 20,
row_spacing: 20
});
let dragSource = new Gtk.DragSource({
actions: Gdk.DragAction.MOVE
});
this.add_controller(dragSource);
let dropTarget = new Gtk.DropTargetAsync({
actions: Gdk.DragAction.MOVE
});
this.add_controller(dropTarget);
this.x = 0;
Gtk.ListBoxRow.prototype.set_child.call(this, this._grid);
dragSource.connect("drag-begin", (self, gdkDrag) => {
//get listbox parent
let listBox = self.get_widget().get_parent();
//get widgets parent - the listBoxDragRow
listBox.dragRow = this;
let alloc = self.get_widget().get_allocation();
let dragWidget = self.get_widget().createDragRow(alloc);
listBox.dragWidget = dragWidget;
let icon = Gtk.DragIcon.get_for_drag(gdkDrag);
icon.set_child(dragWidget);
gdkDrag.set_hotspot(listBox.dragX, listBox.dragY);
});
dragSource.connect("prepare", (self, x, y) => {
//get listbox parent
this.set_state_flags(Gtk.StateFlags.NORMAL, true);
let listBox = self.get_widget().get_parent();
//store drag start cursor location
listBox.dragX = x;
listBox.dragY = y;
return new Gdk.ContentProvider(Arc_Menu_FrameBoxDragRow);
});
dragSource.connect("drag-end", (self, gdkDrag, deleteData)=> {
deleteData = true;
let listBox = self.get_widget().get_parent();
listBox.dragWidget = null;
listBox.drag_unhighlight_row();
});
dropTarget.connect("drag-enter", (self, gdkDrop, x, y, selection, info, time)=> {
let listBox = self.get_widget().get_parent();
let widget = self.get_widget();
listBox.startIndex = widget.get_index();
listBox.drag_highlight_row(widget);
if(!scrolledWindow)
return true;
let height = widget.get_height();
let scrollHeight = scrolledWindow.get_height();
let widgetLoc = widget.get_index() * height;
let value = scrolledWindow.vadjustment.value;
if((widgetLoc + (height * 4)) > (value + scrollHeight))
scrolledWindow.vadjustment.value += height;
else if((widgetLoc - (height * 2)) < value)
scrolledWindow.vadjustment.value -= height;
});
dropTarget.connect("drag-leave", (self, gdkDrop, x, y, selection, info, time)=> {
let listBox = self.get_widget().get_parent();
listBox.drag_unhighlight_row();
});
dropTarget.connect("drop", (self, gdkDrop, x, y, selection, info, time)=> {
//get listbox parent
let listBox = this.get_parent();
let index = this.get_index();
if(index === listBox.dragRow.get_index()){
gdkDrop.read_value_async(Arc_Menu_FrameBoxDragRow, 1, null, ()=>{
gdkDrop.finish(Gdk.DragAction.MOVE);
});
return true;
}
listBox.remove(listBox.dragRow);
listBox.show();
listBox.insert(listBox.dragRow, index);
this.resetButton?.set_sensitive(true);
this.saveButton.set_sensitive(true);
gdkDrop.read_value_async(Arc_Menu_FrameBoxDragRow, 1, null, ()=>{
gdkDrop.finish(Gdk.DragAction.MOVE);
});
return true;
});
}
createDragRow(alloc){
let dragWidget = new Gtk.ListBox();
dragWidget.set_size_request(alloc.width, alloc.height);
let dragRow = new FrameBoxRow();
dragWidget.append(dragRow);
dragWidget.drag_highlight_row(dragRow);
let image = new Gtk.Image( {
gicon: this._gicon,
pixel_size: 22
});
let imageBox = new Gtk.Box({
margin_start: 0,
hexpand: false,
vexpand: true,
spacing: 5,
});
let dragImage = new Gtk.Image( {
gicon: Gio.icon_new_for_string("drag-symbolic"),
pixel_size: 12
});
imageBox.append(dragImage);
imageBox.append(image);
dragRow.add(imageBox);
let label = new Gtk.Label({
use_markup: true,
xalign: 0,
hexpand: true,
label: _(this._name)
});
dragRow.add(label);
let grid = new Gtk.Grid({
margin_top: 0,
margin_bottom: 0,
vexpand: false,
hexpand: false,
column_spacing: 10
})
let editButton = new Button({
icon_name: 'view-more-symbolic'
});
grid.attach(editButton, 0, 0, 1, 1);
if(this.hasSwitch){
let modifyButton = new Gtk.Switch({
valign: Gtk.Align.CENTER,
margin_start: 10,
active: this.switchActive
});
grid.insert_column(0);
grid.attach(Gtk.Separator.new(Gtk.Orientation.VERTICAL), 0, 0, 1, 1);
grid.insert_column(0);
grid.attach(modifyButton, 0, 0, 1, 1);
}
if(this.hasEditButton){
let editButton = new Button({
icon_name: 'text-editor-symbolic',
});
grid.insert_column(0);
grid.attach(Gtk.Separator.new(Gtk.Orientation.VERTICAL), 0, 0, 1, 1);
grid.insert_column(0);
grid.attach(editButton, 0, 0, 1, 1);
}
dragRow.add(grid);
return dragWidget;
}
add(widget) {
this._grid.attach(widget, this.x, 0, 1, 1);
this.x++;
}
setVerticalAlignmentBottom(){
this._grid.vexpand = true;
this._grid.valign = Gtk.Align.END;
}
});
var EditEntriesBox = GObject.registerClass({
Signals: {
'modify': {},
'change': {},
'move-up': {},
'move-down': {},
'delete': {},
},
}, class Arc_Menu_EditEntriesBox extends Gtk.Grid{
_init(params){
super._init({
margin_top: 0,
margin_bottom: 0,
vexpand: false,
hexpand: false,
column_spacing: 10
});
let editPopover = new Gtk.Popover();
let frameRow = params.frameRow;
let frame = params.frame;
let buttons = params.buttons;
let modifyButton, deleteButton, changeButton;
if(params.modifyButton){
modifyButton = new Gtk.Button({
label: _("Modify"),
has_frame: false
});
modifyButton.connect('clicked', () => {
editPopover.popdown();
this.emit('modify');
});
}
if(params.changeButton){
changeButton = new Button({
icon_name: 'text-editor-symbolic',
});
changeButton.connect('clicked', () => {
editPopover.popdown();
this.emit('change');
});
}
let editButton = new Gtk.MenuButton({
icon_name: 'view-more-symbolic',
popover: editPopover,
});
this.attach(editButton, 0, 0, 1, 1);
let editPopoverBox = new Gtk.Box({
orientation: Gtk.Orientation.VERTICAL
});
editPopover.set_child(editPopoverBox);
let moveUpButton = new Gtk.Button({
label: _("Move Up"),
has_frame: false
});
moveUpButton.connect('clicked', ()=> {
this.emit('move-up');
let index = frameRow.get_index();
if(index > 0){
frame.remove(frameRow);
frame.insert(frameRow, index - 1);
}
frame.show();
buttons.forEach(button => button.set_sensitive(true));
editPopover.popdown();
});
let moveDownButton = new Gtk.Button({
label: _("Move Down"),
has_frame: false
});
moveDownButton.connect('clicked', ()=> {
this.emit('move-down');
let index = frameRow.get_index();
if(index + 1 < frame.count) {
frame.remove(frameRow);
frame.insert(frameRow, index + 1);
}
frame.show();
buttons.forEach(button => button.set_sensitive(true));
editPopover.popdown();
});
if(params.deleteButton){
deleteButton = new Gtk.Button({
label: _("Delete"),
has_frame: false,
});
deleteButton.connect('clicked', ()=> {
this.emit('delete');
frame.remove(frameRow);
frame.show();
buttons.forEach(button => button.set_sensitive(true));
editPopover.popdown();
});
}
if(params.changeButton){
this.insert_column(0);
this.attach(Gtk.Separator.new(Gtk.Orientation.VERTICAL), 0, 0, 1, 1);
this.insert_column(0);
this.attach(changeButton, 0, 0, 1, 1);
}
if(params.modifyButton){
editPopoverBox.append(modifyButton);
editPopoverBox.append(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL));
}
editPopoverBox.append(moveUpButton);
editPopoverBox.append(moveDownButton);
if(params.deleteButton){
editPopoverBox.append(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL));
editPopoverBox.append(deleteButton);
}
}
});
var StackListBox = GObject.registerClass(class Arc_Menu_StackListBox extends Gtk.ListBox{
_init(widget, params){
super._init(params);
this.valign = Gtk.Align.FILL;
this.vexpand = true;
this.hexpand = false;
this.settingsFrameStack = widget.settingsFrameStack;
this.settingsListStack = widget.settingsListStack
this.connect("row-selected", (self, row) => {
if(row){
let stackName = row.stackName;
this.settingsFrameStack.set_visible_child_name(stackName);
if(row.nextPage){
if(widget.backButton.get_parent())
widget.leftHeaderBox.remove(widget.backButton);
widget.leftHeaderBox.prepend(widget.backButton);
this.settingsListStack.set_visible_child_name(row.nextPage);
this.settingsListStack.get_child_by_name(row.nextPage).listBox.selectFirstRow();
}
}
});
this.scrollWindow = new Gtk.ScrolledWindow({
valign: Gtk.Align.FILL,
vexpand: true
});
this.scrollWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.scrollWindow.set_child(this);
this.scrollWindow.listBox = this;
}
getRowAtIndex(index){
return this.get_row_at_index(index).get_children()[0];
}
getSelectedRow(){
return this.get_selected_row().get_children()[0];
}
selectFirstRow(){
this.select_row(this.get_row_at_index(0));
}
selectRowAtIndex(index){
this.select_row(this.get_row_at_index(index));
}
addRow(name, translateableName, iconName, nextPage){
let row1 = new Gtk.ListBoxRow();
this.append(row1);
let row = new Gtk.Grid({
margin_top: 12,
margin_bottom: 12,
margin_start: 12,
margin_end: 12,
column_spacing: 10
});
row1.set_child(row);
row1.stackName = name;
row1.translateableName = translateableName;
let image = new Gtk.Image({
icon_name: iconName
});
let label = new Gtk.Label({
label: translateableName,
halign: Gtk.Align.START,
});
row.attach(image, 0, 0, 1, 1);
row.attach(label, 1, 0, 1, 1);
if(nextPage){
row1.nextPage = nextPage;
let image2 = new Gtk.Image({
gicon: Gio.icon_new_for_string('go-next-symbolic'),
halign: Gtk.Align.END,
hexpand: true
});
row.attach(image2, 2, 0, 1, 1);
}
}
setSeparatorIndices(indexArray){
this.set_header_func((_row, _before) =>{
for(let i = 0; i < indexArray.length; i++){
if(_row.get_index() === indexArray[i]){
let sep = Gtk.Separator.new(Gtk.Orientation.HORIZONTAL);
sep.show();
_row.set_header(sep);
}
}
});
}
});
var TileGrid = GObject.registerClass(class Arc_Menu_TileGrid extends Gtk.FlowBox{
_init(maxColumns) {
super._init({
row_spacing: 5,
column_spacing: 5,
vexpand: true,
hexpand: true,
valign: Gtk.Align.CENTER,
halign: Gtk.Align.CENTER,
max_children_per_line: maxColumns,
homogeneous: true,
selection_mode: Gtk.SelectionMode.NONE
});
}
});
var IconGrid = GObject.registerClass(class Arc_Menu_IconGrid extends Gtk.FlowBox{
_init() {
super._init({
max_children_per_line: 7,
row_spacing: 10,
column_spacing: 10,
vexpand: true,
hexpand: false,
valign: Gtk.Align.START,
halign: Gtk.Align.CENTER,
homogeneous: true,
selection_mode: Gtk.SelectionMode.SINGLE
});
this.childrenCount = 0;
}
add(widget){
this.insert(widget, -1);
this.childrenCount++;
}
});
var Tile = GObject.registerClass(class Arc_Menu_Tile extends FrameBox{
_init(name, file, width, height, layout) {
super._init({
hexpand: true,
vexpand: false,
halign: Gtk.Align.FILL,
valign: Gtk.Align.CENTER
});
this.box = new FrameBoxRow();
this.box.activatable = true;
this.box._grid.row_spacing = 0;
this.box._grid.orientation = Gtk.Orientation.VERTICAL,
this.activatable = true;
this.name = name;
this.layout = layout;
this._image = new Gtk.Image({
gicon: Gio.icon_new_for_string(file),
pixel_size: width
});
this._label = new Gtk.Label({ label: _(this.name) });
this.box._grid.attach(this._image, 0, 0, 1, 1);
this.box._grid.attach(this._label, 0, 1, 1, 1);
this._listBox.append(this.box);
}
});
var LayoutTile = GObject.registerClass(class Arc_Menu_LayoutTile extends FrameBox{
_init(name, file, layout) {
super._init({
halign: Gtk.Align.FILL,
valign: Gtk.Align.CENTER,
hexpand: true,
vexpand: false
});
this._listBox.set_selection_mode(Gtk.SelectionMode.NONE);
this.name = name;
this.layout = layout.MENU_TYPE;
this.box = new FrameBoxRow();
this.box.activatable = true;
this.box._grid.row_spacing = 0;
this.box._grid.column_spacing = 0;
this._image = new Gtk.Image({
hexpand: false,
halign: Gtk.Align.START,
gicon: Gio.icon_new_for_string(file),
pixel_size: 46
});
let titleLabel = new Gtk.Label({
label: "" + _("%s Menu Layouts", layout.TITLE).format(layout.TITLE) + "",
use_markup: true,
hexpand: true,
halign: Gtk.Align.CENTER,
vexpand: true,
valign: Gtk.Align.CENTER,
wrap: true,
})
let goNextImage = new Gtk.Image({
gicon: Gio.icon_new_for_string('go-next-symbolic'),
halign: Gtk.Align.END,
valign: Gtk.Align.CENTER,
hexpand: false,
vexpand: false,
})
this.box._grid.attach(this._image, 0, 0, 1, 2);
this.box._grid.attach(titleLabel, 1, 0, 1, 1);
this.box._grid.attach(goNextImage, 2, 0, 1, 2);
this._listBox.append(this.box);
}
});