|
|
@@ -63,6 +63,11 @@ public:
|
|
|
// Safety check during destruction
|
|
|
if (!core_ || !app_) return;
|
|
|
|
|
|
+ // Handle layout changes
|
|
|
+ if (event == EditorEvent::WindowLayoutChanged) {
|
|
|
+ rebuild_layout();
|
|
|
+ }
|
|
|
+
|
|
|
// Request redraw on most events
|
|
|
if (drawing_area_) {
|
|
|
drawing_area_->queue_draw();
|
|
|
@@ -131,7 +136,8 @@ private:
|
|
|
std::string message_line_;
|
|
|
|
|
|
// Member variables
|
|
|
- Gtk::DrawingArea* drawing_area_ = nullptr;
|
|
|
+ Gtk::DrawingArea* drawing_area_ = nullptr; // For single-window compatibility
|
|
|
+ Gtk::Widget* content_widget_ = nullptr; // Will be either drawing_area_ or a split container
|
|
|
double char_width_ = 0;
|
|
|
double line_height_ = 0;
|
|
|
double ascent_ = 0;
|
|
|
@@ -153,23 +159,28 @@ protected:
|
|
|
// We just keep a raw pointer for access, but don't manage its lifetime
|
|
|
window_ = new LumacsWindow(app_);
|
|
|
|
|
|
- // Create drawing area for text rendering
|
|
|
- // Gtk::make_managed ties the widget's lifetime to its parent container
|
|
|
- drawing_area_ = Gtk::make_managed<Gtk::DrawingArea>();
|
|
|
- drawing_area_->set_draw_func(sigc::mem_fun(*this, &GtkEditor::on_draw));
|
|
|
- drawing_area_->set_focusable(true);
|
|
|
+ // Build initial layout (single window)
|
|
|
+ rebuild_layout();
|
|
|
|
|
|
- // Add to window - window becomes the parent and will manage drawing_area lifetime
|
|
|
- window_->set_child(*drawing_area_);
|
|
|
-
|
|
|
- // Input handling
|
|
|
- auto controller = Gtk::EventControllerKey::create();
|
|
|
- controller->signal_key_pressed().connect(sigc::mem_fun(*this, &GtkEditor::on_key_pressed), false);
|
|
|
- drawing_area_->add_controller(controller);
|
|
|
+ // Handle window close event
|
|
|
+ window_->signal_close_request().connect([this]() -> bool {
|
|
|
+ // Cleanup before closing
|
|
|
+ if (cursor_timer_connection_.connected()) {
|
|
|
+ cursor_timer_connection_.disconnect();
|
|
|
+ }
|
|
|
+ core_ = nullptr;
|
|
|
+ drawing_area_ = nullptr;
|
|
|
+ content_widget_ = nullptr;
|
|
|
+
|
|
|
+ // Allow window to close
|
|
|
+ return false; // false means "allow close"
|
|
|
+ }, false);
|
|
|
|
|
|
// Show window
|
|
|
window_->present();
|
|
|
- drawing_area_->grab_focus();
|
|
|
+ if (drawing_area_) {
|
|
|
+ drawing_area_->grab_focus();
|
|
|
+ }
|
|
|
|
|
|
// Set up cursor blinking timer (500ms intervals like Emacs)
|
|
|
cursor_timer_connection_ = Glib::signal_timeout().connect(
|
|
|
@@ -314,6 +325,107 @@ protected:
|
|
|
return true; // Continue timer
|
|
|
}
|
|
|
|
|
|
+ // Rebuild the GTK layout to match the core's window tree
|
|
|
+ void rebuild_layout() {
|
|
|
+ if (!core_ || !window_) return;
|
|
|
+
|
|
|
+ auto root_layout = core_->root_layout();
|
|
|
+ if (!root_layout) return;
|
|
|
+
|
|
|
+ // Remove existing content
|
|
|
+ if (content_widget_) {
|
|
|
+ window_->unset_child();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create new layout based on the tree
|
|
|
+ content_widget_ = create_widget_for_layout_node(root_layout);
|
|
|
+ if (content_widget_) {
|
|
|
+ window_->set_child(*content_widget_);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create GTK widget tree from LayoutNode tree
|
|
|
+ Gtk::Widget* create_widget_for_layout_node(std::shared_ptr<LayoutNode> node) {
|
|
|
+ if (!node) return nullptr;
|
|
|
+
|
|
|
+ if (node->type == LayoutNode::Type::Leaf) {
|
|
|
+ // Create a new DrawingArea for this window
|
|
|
+ auto drawing_area = Gtk::make_managed<Gtk::DrawingArea>();
|
|
|
+
|
|
|
+ // Set up drawing for this specific window
|
|
|
+ drawing_area->set_draw_func([this, node](const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
|
|
|
+ if (node && node->window) {
|
|
|
+ draw_window(cr, width, height, node->window);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ drawing_area->set_focusable(true);
|
|
|
+
|
|
|
+ // Add input handling
|
|
|
+ auto controller = Gtk::EventControllerKey::create();
|
|
|
+ controller->signal_key_pressed().connect([this, node](guint keyval, guint keycode, Gdk::ModifierType state) -> bool {
|
|
|
+ // Set this window as active when it receives input
|
|
|
+ if (node && node->window && core_) {
|
|
|
+ // TODO: Set active window in core
|
|
|
+ }
|
|
|
+ return on_key_pressed(keyval, keycode, state);
|
|
|
+ }, false);
|
|
|
+ drawing_area->add_controller(controller);
|
|
|
+
|
|
|
+ // Store reference for single-window compatibility
|
|
|
+ if (!drawing_area_) {
|
|
|
+ drawing_area_ = drawing_area;
|
|
|
+ }
|
|
|
+
|
|
|
+ return drawing_area;
|
|
|
+ } else {
|
|
|
+ // Create a paned container for splits
|
|
|
+ Gtk::Paned* paned = nullptr;
|
|
|
+ if (node->type == LayoutNode::Type::HorizontalSplit) {
|
|
|
+ paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::VERTICAL);
|
|
|
+ } else { // VerticalSplit
|
|
|
+ paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::HORIZONTAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Recursively create children
|
|
|
+ auto child1 = create_widget_for_layout_node(node->child1);
|
|
|
+ auto child2 = create_widget_for_layout_node(node->child2);
|
|
|
+
|
|
|
+ if (child1) paned->set_start_child(*child1);
|
|
|
+ if (child2) paned->set_end_child(*child2);
|
|
|
+
|
|
|
+ // Set initial position based on ratio
|
|
|
+ paned->set_position(static_cast<int>(500 * node->ratio)); // Default size
|
|
|
+
|
|
|
+ return paned;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Draw a specific window (factored out from on_draw)
|
|
|
+ void draw_window(const Cairo::RefPtr<Cairo::Context>& cr, int /*width*/, int /*height*/, std::shared_ptr<Window> window) {
|
|
|
+ if (!core_ || !window) return;
|
|
|
+
|
|
|
+ // Fill background
|
|
|
+ auto theme = core_->active_theme();
|
|
|
+ Color bg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
|
|
|
+
|
|
|
+ cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
|
|
|
+ cr->paint();
|
|
|
+
|
|
|
+ // For now, just draw a simple indicator showing the buffer name
|
|
|
+ auto layout = Pango::Layout::create(cr);
|
|
|
+ Pango::FontDescription font_desc("Monospace 12");
|
|
|
+ layout->set_font_description(font_desc);
|
|
|
+
|
|
|
+ std::string buffer_name = "Window: " + window->buffer().name();
|
|
|
+ layout->set_text(buffer_name);
|
|
|
+
|
|
|
+ Color fg = theme ? theme->get_fg_color(ThemeElement::Normal) : Color(255, 255, 255);
|
|
|
+ cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
|
|
|
+ cr->move_to(10, 10);
|
|
|
+ layout->show_in_cairo_context(cr);
|
|
|
+ }
|
|
|
+
|
|
|
// Render the modeline above minibuffer
|
|
|
void render_modeline(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height,
|
|
|
const Glib::RefPtr<Pango::Layout>& layout) {
|