From 54a1e7c90dd04f382fd317f0be01188ca317ba30 Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 7 Jul 2015 23:15:58 +0200 Subject: [PATCH] Made new selectiondialog for code-completion. Might be in need of some smaller fixes. Max width set to halv of width of sourceview. --- juci/selectiondialog.cc | 229 ++++++++++++++++++++++++++++------------ juci/selectiondialog.h | 25 +++-- juci/source.cc | 27 +++-- juci/source.h | 2 + 4 files changed, 198 insertions(+), 85 deletions(-) diff --git a/juci/selectiondialog.cc b/juci/selectiondialog.cc index 28a45c1..d812b20 100644 --- a/juci/selectiondialog.cc +++ b/juci/selectiondialog.cc @@ -1,87 +1,180 @@ #include "selectiondialog.h" +#include +using namespace std; -SelectionDialog::SelectionDialog(Gtk::TextView& text_view): Gtk::Dialog(), text_view(text_view), - list_view_text(1, false, Gtk::SelectionMode::SELECTION_SINGLE) { - property_decorated()=false; - set_skip_taskbar_hint(true); - scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_NEVER); - list_view_text.set_enable_search(true); - list_view_text.set_headers_visible(false); - list_view_text.set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL); - list_view_text.set_activate_on_single_click(true); +SelectionDialog::SelectionDialog(Gtk::TextView& text_view): text_view(text_view) { + } -void SelectionDialog::show(const std::map& rows) { - for (auto &i : rows) { - list_view_text.append(i.first); - } - scrolled_window.add(list_view_text); - get_vbox()->pack_start(scrolled_window); - set_transient_for((Gtk::Window&)(*text_view.get_toplevel())); - show_all(); - int popup_x = get_width(); - int popup_y = rows.size() * 20; - adjust(popup_x, popup_y); +void SelectionDialog::show() { + if(rows.size()==0) + return; + + window=std::unique_ptr(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP)); + scrolled_window=std::unique_ptr(new Gtk::ScrolledWindow()); + list_view_text=std::unique_ptr(new Gtk::ListViewText(1, false, Gtk::SelectionMode::SELECTION_SINGLE)); - list_view_text.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { - if(on_select) - on_select(list_view_text); - response(Gtk::RESPONSE_DELETE_EVENT); + window->set_default_size(0, 0); + window->property_decorated()=false; + window->set_skip_taskbar_hint(true); + scrolled_window->set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC); + list_view_text->set_enable_search(true); + list_view_text->set_headers_visible(false); + list_view_text->set_hscroll_policy(Gtk::ScrollablePolicy::SCROLL_NATURAL); + list_view_text->set_activate_on_single_click(true); + list_view_text->set_search_entry(search_entry); + list_view_text->set_hover_selection(true); + list_view_text->set_rules_hint(true); + //list_view_text->set_fixed_height_mode(true); //TODO: This is buggy on OS X, remember to post an issue on GTK+ 3 + + list_view_text->signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { + if(shown) { + select(); + } + }); + list_view_text->signal_realize().connect([this](){ + resize(); }); - signal_focus_out_event().connect(sigc::mem_fun(*this, &SelectionDialog::close), false); + if(start_mark) + text_view.get_buffer()->delete_mark(start_mark); + start_mark=text_view.get_buffer()->create_mark(text_view.get_buffer()->get_insert()->get_iter()); + start_offset=start_mark->get_iter().get_offset(); + list_view_text->clear_items(); + for (auto &i : rows) { + list_view_text->append(i.first); + } - run(); + scrolled_window->add(*list_view_text); + window->add(*scrolled_window); + + if(rows.size()>0) { + list_view_text->get_selection()->select(*list_view_text->get_model()->children().begin()); + list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]); + } + + move(); + + window->show_all(); + shown=true; } -bool SelectionDialog::close(GdkEventFocus*) { - response(Gtk::RESPONSE_DELETE_EVENT); - return true; +void SelectionDialog::hide() { + window->hide(); + shown=false; } -void SelectionDialog::adjust(int current_x, int current_y) { - INFO("SelectionDialog set size"); - int view_x = text_view.get_width(); - int view_y = 150; - bool is_never_scroll_x = true; - bool is_never_scroll_y = true; - if (current_x > view_x) { - current_x = view_x; - is_never_scroll_x = false; +void SelectionDialog::select(bool hide_window) { + auto selected=list_view_text->get_selected(); + if(selected.size()>0) { + std::string select = rows.at(list_view_text->get_text(selected[0])); + text_view.get_buffer()->erase(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); + text_view.get_buffer()->insert(start_mark->get_iter(), select); } - if (current_y > view_y) { - current_y = view_y; - is_never_scroll_y = false; + if(hide_window) { + hide(); } - scrolled_window.set_size_request(current_x, current_y); - if (!is_never_scroll_x && !is_never_scroll_y) { - scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC); - } else if (!is_never_scroll_x && is_never_scroll_y) { - scrolled_window.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_NEVER); - } else if (is_never_scroll_x && !is_never_scroll_y) { - scrolled_window.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); +} + +bool SelectionDialog::on_key_release(GdkEventKey* key) { + if(key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_Up) + return false; + + if(start_offset>text_view.get_buffer()->get_insert()->get_iter().get_offset()) { + hide(); + } + else { + auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); + if(text.size()>0) { + search_entry.set_text(text); + list_view_text->set_search_entry(search_entry); + } } - INFO("SelectionDialog set position"); - Gdk::Rectangle temp1, temp2; - text_view.get_cursor_locations(text_view.get_buffer()->get_insert()->get_iter(), temp1, temp2); - int view_edge_x = 0; - int view_edge_y = 0; - int x, y; - text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_WIDGET, - temp1.get_x(), temp1.get_y(), x, y); - Glib::RefPtr gdkw = text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_WIDGET); - gdkw->get_origin(view_edge_x, view_edge_y); + return false; +} - x += view_edge_x; - y += view_edge_y; - if ((view_edge_x-x)*-1 > text_view.get_width()-current_x) { - x -= current_x; - if (x < view_edge_x) x = view_edge_x; +bool SelectionDialog::on_key_press(GdkEventKey* key) { + if(key->keyval>=GDK_KEY_0 && key->keyval<=GDK_KEY_9) + return false; + if(key->keyval>=GDK_KEY_A && key->keyval<=GDK_KEY_Z) + return false; + if(key->keyval>=GDK_KEY_a && key->keyval<=GDK_KEY_z) + return false; + if(key->keyval==GDK_KEY_Shift_L || key->keyval==GDK_KEY_Shift_R || key->keyval==GDK_KEY_Alt_L || key->keyval==GDK_KEY_Alt_R || key->keyval==GDK_KEY_Control_L || key->keyval==GDK_KEY_Control_R || key->keyval==GDK_KEY_Meta_L || key->keyval==GDK_KEY_Meta_R) + return false; + if(key->keyval==GDK_KEY_underscore) + return false; + if(key->keyval==GDK_KEY_BackSpace) + return false; + if(key->keyval==GDK_KEY_Down) { + auto it=list_view_text->get_selection()->get_selected(); + if(it) { + it++; + if(it) { + list_view_text->get_selection()->select(it); + list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]); + } + } + select(false); + return true; + } + if(key->keyval==GDK_KEY_Up) { + auto it=list_view_text->get_selection()->get_selected(); + if(it) { + it--; + if(it) { + list_view_text->get_selection()->select(it); + list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]); + } + } + select(false); + return true; } - if ((view_edge_y-y)*-1 > text_view.get_height()-current_y) { - y -= (current_y+14) + 15; - if (x < view_edge_y) y = view_edge_y +15; + if(key->keyval==GDK_KEY_Return) { + select(); + return true; } - move(x, y+15); -} \ No newline at end of file + if(key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) { + select(); + return true; + } + hide(); + return false; +} + +void SelectionDialog::move() { + INFO("SelectionDialog set position"); + Gdk::Rectangle rectangle; + text_view.get_iter_location(start_mark->get_iter(), rectangle); + int buffer_x=rectangle.get_x(); + int buffer_y=rectangle.get_y()+rectangle.get_height(); + int window_x, window_y; + text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, buffer_x, buffer_y, window_x, window_y); + int root_x, root_y; + text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y); + window->move(root_x, root_y+1); //TODO: replace 1 with some margin +} + +void SelectionDialog::resize() { + INFO("SelectionDialog set size"); + + if(list_view_text->get_realized()) { + int row_width=0, row_height; + Gdk::Rectangle rect; + list_view_text->get_cell_area(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin()), *(list_view_text->get_column(0)), rect); + row_width=rect.get_width(); + row_height=rect.get_height(); + + row_width+=rect.get_x()*2; //TODO: Add correct margin x and y + row_height+=rect.get_y()*2; + + if(row_width>text_view.get_width()/2) + row_width=text_view.get_width()/2; + else + scrolled_window->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); + + int window_height=std::min(row_height*(int)rows.size(), row_height*10); + window->resize(row_width, window_height); + } +} diff --git a/juci/selectiondialog.h b/juci/selectiondialog.h index f8479ce..e13bc95 100644 --- a/juci/selectiondialog.h +++ b/juci/selectiondialog.h @@ -3,22 +3,31 @@ #include "gtkmm.h" #include "logging.h" -#include "source.h" -class SelectionDialog : public Gtk::Dialog { +class SelectionDialog { public: SelectionDialog(Gtk::TextView& text_view); - void show(const std::map& rows); + void show(); + void hide(); bool close(GdkEventFocus*); + void select(bool hide_window=true); + void move(); + bool on_key_release(GdkEventKey* key); + bool on_key_press(GdkEventKey* key); - std::function on_select; - + std::map rows; + + bool shown=false; + Gtk::Entry search_entry; + Glib::RefPtr start_mark; + int start_offset; private: - void adjust(int current_x, int current_y); + void resize(); Gtk::TextView& text_view; - Gtk::ScrolledWindow scrolled_window; - Gtk::ListViewText list_view_text; + std::unique_ptr window; + std::unique_ptr scrolled_window; + std::unique_ptr list_view_text; }; #endif // JUCI_SELECTIONDIALOG_H_ \ No newline at end of file diff --git a/juci/source.cc b/juci/source.cc index 3fff1b6..f0009de 100644 --- a/juci/source.cc +++ b/juci/source.cc @@ -6,7 +6,6 @@ #include "logging.h" #include #include -#include "selectiondialog.h" #include "singletons.h" namespace sigc { @@ -147,7 +146,7 @@ bool Source::View::on_key_press(GdkEventKey* key) { clang::Index Source::ClangView::clang_index(0, 0); Source::ClangView::ClangView(const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal): -Source::View(file_path, project_path), terminal(terminal), +Source::View(file_path, project_path), terminal(terminal), selection_dialog(*this), parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { int start_offset = get_source_buffer()->begin().get_offset(); int end_offset = get_source_buffer()->end().get_offset(); @@ -443,6 +442,9 @@ bool Source::ClangView::clangview_on_motion_notify_event(GdkEventMotion* event) void Source::ClangView::clangview_on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark) { if(mark->get_name()=="insert") { + if(selection_dialog.shown) { + selection_dialog.hide(); + } on_mark_set_timeout_connection.disconnect(); on_mark_set_timeout_connection=Glib::signal_timeout().connect([this]() { if(clang_updated) { @@ -472,6 +474,9 @@ bool Source::ClangView::clangview_on_focus_out_event(GdkEventFocus* event) { } bool Source::ClangView::clangview_on_scroll_event(GdkEventScroll* event) { + if(selection_dialog.shown) + selection_dialog.move(); + on_mark_set_timeout_connection.disconnect(); type_tooltips.hide(); diagnostic_tooltips.hide(); @@ -511,6 +516,11 @@ highlight_token(clang::Token *token, } bool Source::ClangView::on_key_release(GdkEventKey* key) { + if(selection_dialog.shown) { + if(selection_dialog.on_key_release(key)) + return true; + } + INFO("Source::ClangView::on_key_release getting iters"); // Get function to fill popup with suggests item vector under is for testing Gtk::TextIter beg = get_source_buffer()->get_insert()->get_iter(); @@ -569,13 +579,8 @@ bool Source::ClangView::on_key_release(GdkEventKey* key) { if (rows.empty()) { rows["No suggestions found..."] = ""; } - - SelectionDialog selection_dialog(*this); - selection_dialog.on_select=[this, &rows](Gtk::ListViewText& list_view_text){ - std::string selected = rows.at(list_view_text.get_text(list_view_text.get_selected()[0])); - get_source_buffer()->insert_at_cursor(selected); - }; - selection_dialog.show(rows); + selection_dialog.rows=std::move(rows); + selection_dialog.show(); return true; } @@ -583,6 +588,10 @@ bool Source::ClangView::on_key_release(GdkEventKey* key) { //Clang indentation //TODO: replace indentation methods with a better implementation or maybe use libclang bool Source::ClangView::on_key_press(GdkEventKey* key) { + if(selection_dialog.shown) { + if(selection_dialog.on_key_press(key)) + return true; + } auto config=Singletons::Config::source(); const std::regex bracket_regex(std::string("^(")+config->tab_char+"*).*\\{ *$"); const std::regex no_bracket_statement_regex(std::string("^(")+config->tab_char+"*)(if|for|else if|catch|while) *\\(.*[^;}] *$"); diff --git a/juci/source.h b/juci/source.h index ceafe11..f06d23b 100644 --- a/juci/source.h +++ b/juci/source.h @@ -12,6 +12,7 @@ #include "gtksourceviewmm.h" #include "terminal.h" #include "tooltips.h" +#include "selectiondialog.h" namespace Source { class Config { @@ -93,6 +94,7 @@ namespace Source { int end_offset, clang::Index *index); std::vector get_autocomplete_suggestions(int line_number, int column); + SelectionDialog selection_dialog; int reparse(const std::map &buffers); std::vector extract_tokens(int, int); void update_syntax(const std::vector &locations);