From 53d34a0885962b49015b89b884049a47f956da90 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 10 Feb 2019 14:28:39 +0100 Subject: [PATCH] Port Terminal to LibGUI. To facilitate listening for action on arbitrary file descriptors, I've added a GNotifier class. It's quite simple but very useful: GNotifier notifier(fd, GNotifier::Read); notifier.on_ready_to_read = [this] (GNotifier& fd) { // read from fd or whatever else you like :^) }; The callback will get invoked by GEventLoop when select() says we have something to read on the fd. --- Applications/Terminal/Makefile | 2 +- Applications/Terminal/Terminal.cpp | 176 +++++++++++++---------------- Applications/Terminal/Terminal.h | 28 ++--- Applications/Terminal/main.cpp | 55 ++++++--- LibGUI/GEventLoop.cpp | 47 +++++++- LibGUI/GEventLoop.h | 6 + LibGUI/GNotifier.cpp | 15 +++ LibGUI/GNotifier.h | 25 ++++ LibGUI/GWidget.cpp | 22 ++-- LibGUI/GWidget.h | 5 +- LibGUI/GWindow.cpp | 25 +++- LibGUI/GWindow.h | 3 + LibGUI/Makefile | 1 + SharedGraphics/Painter.h | 2 + SharedGraphics/Rect.h | 7 ++ 15 files changed, 268 insertions(+), 151 deletions(-) create mode 100644 LibGUI/GNotifier.cpp create mode 100644 LibGUI/GNotifier.h diff --git a/Applications/Terminal/Makefile b/Applications/Terminal/Makefile index 6f8dde1e3e..ac2ed4ba57 100644 --- a/Applications/Terminal/Makefile +++ b/Applications/Terminal/Makefile @@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibC/LibC.a + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Applications/Terminal/Terminal.cpp b/Applications/Terminal/Terminal.cpp index f5d1f09ef3..68ce1d4691 100644 --- a/Applications/Terminal/Terminal.cpp +++ b/Applications/Terminal/Terminal.cpp @@ -8,40 +8,17 @@ #include #include #include -#include +#include +#include //#define TERMINAL_DEBUG -void Terminal::create_window() +Terminal::Terminal(int ptm_fd) + : m_ptm_fd(ptm_fd) + , m_font(Font::default_font()) { - m_pixel_width = m_columns * font().glyph_width() + m_inset * 2; - m_pixel_height = (m_rows * (font().glyph_height() + m_line_spacing)) + (m_inset * 2) - m_line_spacing; + set_fill_with_background_color(false); - GUI_WindowParameters params; - params.rect = { { 300, 300 }, { m_pixel_width, m_pixel_height } }; - params.background_color = 0x000000; - strcpy(params.title, "Terminal"); - m_window_id = gui_create_window(¶ms); - ASSERT(m_window_id > 0); - if (m_window_id < 0) { - perror("gui_create_window"); - exit(1); - } - - // NOTE: We never release the backing store. - GUI_WindowBackingStoreInfo info; - int rc = gui_get_window_backing_store(m_window_id, &info); - if (rc < 0) { - perror("gui_get_window_backing_store"); - exit(1); - } - - m_backing = GraphicsBitmap::create_wrapper(info.size, info.pixels); -} - -Terminal::Terminal() - : m_font(Font::default_font()) -{ m_line_height = font().glyph_height() + m_line_spacing; set_size(80, 25); @@ -54,6 +31,12 @@ Terminal::Terminal() m_lines = new Line*[rows()]; for (size_t i = 0; i < rows(); ++i) m_lines[i] = new Line(columns()); + + m_pixel_width = m_columns * font().glyph_width() + m_inset * 2; + m_pixel_height = (m_rows * (font().glyph_height() + m_line_spacing)) + (m_inset * 2) - m_line_spacing; + + set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed); + set_preferred_size({ m_pixel_width, m_pixel_height }); } Terminal::Line::Line(word columns) @@ -61,7 +44,6 @@ Terminal::Line::Line(word columns) { characters = new byte[length]; attributes = new Attribute[length]; - did_paint = false; memset(characters, ' ', length); } @@ -623,13 +605,49 @@ bool Terminal::Line::has_only_one_background_color() const return true; } -void Terminal::paint() +void Terminal::event(GEvent& event) +{ + if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) { + m_in_active_window = event.type() == GEvent::WindowBecameActive; + invalidate_cursor(); + update(); + } + return GWidget::event(event); +} + +void Terminal::keydown_event(GKeyEvent& event) +{ + char ch = !event.text().is_empty() ? event.text()[0] : 0; + if (event.ctrl()) { + if (ch >= 'a' && ch <= 'z') { + ch = ch - 'a' + 1; + } else if (ch == '\\') { + ch = 0x1c; + } + } + switch (event.key()) { + case KeyCode::Key_Up: + write(m_ptm_fd, "\033[A", 3); + break; + case KeyCode::Key_Down: + write(m_ptm_fd, "\033[B", 3); + break; + case KeyCode::Key_Right: + write(m_ptm_fd, "\033[C", 3); + break; + case KeyCode::Key_Left: + write(m_ptm_fd, "\033[D", 3); + break; + default: + write(m_ptm_fd, &ch, 1); + break; + } +} + +void Terminal::paint_event(GPaintEvent&) { Rect rect { 0, 0, m_pixel_width, m_pixel_height }; - Painter painter(*m_backing); - - for (size_t i = 0; i < rows(); ++i) - line(i).did_paint = false; + Painter painter(*this); if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) { int first_scanline = m_inset; @@ -637,11 +655,11 @@ void Terminal::paint() int num_rows_to_memcpy = m_rows - m_rows_to_scroll_backing_store; int scanlines_to_copy = (num_rows_to_memcpy * m_line_height) - m_line_spacing; fast_dword_copy( - m_backing->scanline(first_scanline), - m_backing->scanline(second_scanline), + painter.target()->scanline(first_scanline), + painter.target()->scanline(second_scanline), scanlines_to_copy * m_pixel_width ); - m_need_full_invalidation = true; + m_need_full_flush = true; line(max(0, m_cursor_row - m_rows_to_scroll_backing_store)).dirty = true; } m_rows_to_scroll_backing_store = 0; @@ -660,7 +678,6 @@ void Terminal::paint() for (word column = 0; column < m_columns; ++column) { bool should_reverse_fill_for_cursor = m_in_active_window && row == m_cursor_row && column == m_cursor_column; auto& attribute = line.attributes[column]; - line.did_paint = true; char ch = line.characters[column]; auto character_rect = glyph_rect(row, column); if (!has_only_one_background_color || should_reverse_fill_for_cursor) { @@ -678,71 +695,34 @@ void Terminal::paint() painter.draw_rect(cell_rect, lookup_color(line(m_cursor_row).attributes[m_cursor_column].foreground_color)); } - line(m_cursor_row).did_paint = true; - - if (m_belling) { - m_need_full_invalidation = true; + if (m_belling) painter.draw_rect(rect, Color::Red); - } +} - if (m_need_full_invalidation) { - did_paint(); - m_need_full_invalidation = false; +void Terminal::set_window_title(String&& title) +{ + auto* w = window(); + if (!w) return; - } - - Rect painted_rect; - for (int i = 0; i < m_rows; ++i) { - if (line(i).did_paint) - painted_rect = painted_rect.united(row_rect(i)); - } - did_paint(painted_rect); -} - -void Terminal::did_paint(const Rect& a_rect) -{ - GUI_Rect rect = a_rect; - int rc = gui_notify_paint_finished(m_window_id, a_rect.is_null() ? nullptr : &rect); - if (rc < 0) { - perror("gui_notify_paint_finished"); - exit(1); - } -} - -void Terminal::update() -{ - Rect rect; - for (int i = 0; i < m_rows; ++i) { - if (line(i).did_paint) - rect = rect.united(row_rect(i)); - } - GUI_Rect gui_rect = rect; - int rc = gui_invalidate_window(m_window_id, rect.is_null() ? nullptr : &gui_rect); - if (rc < 0) { - perror("gui_invalidate_window"); - exit(1); - } -} - -void Terminal::set_window_title(const String& title) -{ - int rc = gui_set_window_title(m_window_id, title.characters(), title.length()); - if (rc < 0) { - perror("gui_set_window_title"); - exit(1); - } -} - -void Terminal::set_in_active_window(bool b) -{ - if (m_in_active_window == b) - return; - m_in_active_window = b; - invalidate_cursor(); - update(); + w->set_title(move(title)); } void Terminal::invalidate_cursor() { line(m_cursor_row).dirty = true; } + +void Terminal::flush_dirty_lines() +{ + if (m_need_full_flush) { + update(); + m_need_full_flush = false; + return; + } + Rect rect; + for (int i = 0; i < m_rows; ++i) { + if (line(i).dirty) + rect = rect.united(row_rect(i)); + } + update(rect); +} diff --git a/Applications/Terminal/Terminal.h b/Applications/Terminal/Terminal.h index e5e7d81f34..64642104a4 100644 --- a/Applications/Terminal/Terminal.h +++ b/Applications/Terminal/Terminal.h @@ -5,31 +5,33 @@ #include #include #include +#include class Font; -class Terminal { +class Terminal final : public GWidget { public: - Terminal(); - ~Terminal(); + explicit Terminal(int ptm_fd); + virtual ~Terminal() override; void create_window(); - void paint(); void on_char(byte); - void set_in_active_window(bool); - void update(); + void flush_dirty_lines(); private: + virtual void event(GEvent&) override; + virtual void paint_event(GPaintEvent&) override; + virtual void keydown_event(GKeyEvent&) override; + virtual const char* class_name() const override { return "Terminal"; } + Font& font() { return *m_font; } void scroll_up(); void newline(); void set_cursor(unsigned row, unsigned column); void put_character_at(unsigned row, unsigned column, byte ch); void invalidate_cursor(); - void did_paint(const Rect& = Rect()); - void invalidate_window(const Rect& = Rect()); - void set_window_title(const String&); + void set_window_title(String&&); void inject_string(const String&); void unimplemented_escape(); @@ -83,7 +85,6 @@ private: bool has_only_one_background_color() const; byte* characters { nullptr }; Attribute* attributes { nullptr }; - bool did_paint { false }; bool dirty { false }; word length { 0 }; }; @@ -125,9 +126,6 @@ private: byte* m_horizontal_tabs { nullptr }; bool m_belling { false }; - int m_window_id { 0 }; - RetainPtr m_backing; - int m_pixel_width { 0 }; int m_pixel_height { 0 }; int m_rows_to_scroll_backing_store { 0 }; @@ -136,8 +134,10 @@ private: int m_line_spacing { 4 }; int m_line_height { 0 }; + int m_ptm_fd { -1 }; + bool m_in_active_window { false }; - bool m_need_full_invalidation { false }; + bool m_need_full_flush { false }; RetainPtr m_font; }; diff --git a/Applications/Terminal/main.cpp b/Applications/Terminal/main.cpp index 36cea4a693..c7ad546c8e 100644 --- a/Applications/Terminal/main.cpp +++ b/Applications/Terminal/main.cpp @@ -5,14 +5,15 @@ #include #include #include -#include -#include -#include #include #include #include #include "Terminal.h" #include +#include +#include +#include +#include static void make_shell(int ptm_fd) { @@ -52,11 +53,6 @@ static void make_shell(int ptm_fd) } } -static int max(int a, int b) -{ - return a > b ? a : b; -} - int main(int, char**) { int ptm_fd = open("/dev/ptmx", O_RDWR); @@ -67,16 +63,39 @@ int main(int, char**) make_shell(ptm_fd); - int event_fd = open("/dev/gui_events", O_RDONLY); - if (event_fd < 0) { - perror("open"); - return 1; - } + GEventLoop loop; - Terminal terminal; - terminal.create_window(); - terminal.update(); + auto* window = new GWindow; + window->set_should_exit_app_on_close(true); + Terminal terminal(ptm_fd); + window->set_main_widget(&terminal); + + GNotifier ptm_notifier(ptm_fd, GNotifier::Read); + ptm_notifier.on_ready_to_read = [&terminal] (GNotifier& notifier) { + byte buffer[BUFSIZ]; + ssize_t nread = read(notifier.fd(), buffer, sizeof(buffer)); + if (nread < 0) { + dbgprintf("Terminal read error: %s\n", strerror(errno)); + perror("read(ptm)"); + GEventLoop::main().exit(1); + return; + } + if (nread == 0) { + dbgprintf("Terminal: EOF on master pty, closing.\n"); + GEventLoop::main().exit(0); + return; + } + for (ssize_t i = 0; i < nread; ++i) + terminal.on_char(buffer[i]); + terminal.flush_dirty_lines(); + }; + + window->show(); + + return loop.exec(); + +#if 0 for (;;) { fd_set rfds; FD_ZERO(&rfds); @@ -112,11 +131,12 @@ int main(int, char**) perror("read(event)"); return 1; } + assert(nread != 0); assert(nread == sizeof(event)); if (event.type == GUI_Event::Type::Paint) { - terminal.paint(); + terminal.update(); } else if (event.type == GUI_Event::Type::KeyDown) { char ch = event.key.character; if (event.key.ctrl) { @@ -151,5 +171,6 @@ int main(int, char**) } } } +#endif return 0; } diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index 187f1df816..e3db469230 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -2,6 +2,7 @@ #include "GEvent.h" #include "GObject.h" #include "GWindow.h" +#include #include #include #include @@ -60,7 +61,7 @@ int GEventLoop::exec() auto* receiver = queued_event.receiver; auto& event = *queued_event.event; #ifdef GEVENTLOOP_DEBUG - dbgprintf("GEventLoop: GObject{%p} event %u (%s)\n", receiver, (unsigned)event.type(), event.name()); + dbgprintf("GEventLoop: %s{%p} event %u\n", receiver->class_name(), receiver, (unsigned)event.type()); #endif if (!receiver) { switch (event.type()) { @@ -149,12 +150,31 @@ void GEventLoop::handle_mouse_event(const GUI_Event& event, GWindow& window) void GEventLoop::wait_for_event() { fd_set rfds; + fd_set wfds; FD_ZERO(&rfds); - FD_SET(m_event_fd, &rfds); + FD_ZERO(&wfds); + + int max_fd = 0; + auto add_fd_to_set = [&max_fd] (int fd, fd_set& set){ + FD_SET(fd, &set); + if (fd > max_fd) + max_fd = fd; + }; + + add_fd_to_set(m_event_fd, rfds); + for (auto& notifier : m_notifiers) { + if (notifier->event_mask() & GNotifier::Read) + add_fd_to_set(notifier->fd(), rfds); + if (notifier->event_mask() & GNotifier::Write) + add_fd_to_set(notifier->fd(), wfds); + if (notifier->event_mask() & GNotifier::Exceptional) + ASSERT_NOT_REACHED(); + } + struct timeval timeout = { 0, 0 }; if (!m_timers.is_empty()) get_next_timer_expiration(timeout); - int rc = select(m_event_fd + 1, &rfds, nullptr, nullptr, (m_queued_events.is_empty() && m_timers.is_empty()) ? nullptr : &timeout); + int rc = select(m_event_fd + 1, &rfds, &wfds, nullptr, (m_queued_events.is_empty() && m_timers.is_empty()) ? nullptr : &timeout); if (rc < 0) { ASSERT_NOT_REACHED(); } @@ -175,6 +195,17 @@ void GEventLoop::wait_for_event() } } + for (auto& notifier : m_notifiers) { + if (FD_ISSET(notifier->fd(), &rfds)) { + if (notifier->on_ready_to_read) + notifier->on_ready_to_read(*notifier); + } + if (FD_ISSET(notifier->fd(), &wfds)) { + if (notifier->on_ready_to_write) + notifier->on_ready_to_write(*notifier); + } + } + if (!FD_ISSET(m_event_fd, &rfds)) return; @@ -266,3 +297,13 @@ bool GEventLoop::unregister_timer(int timer_id) m_timers.remove(it); return true; } + +void GEventLoop::register_notifier(Badge, GNotifier& notifier) +{ + m_notifiers.set(¬ifier); +} + +void GEventLoop::unregister_notifier(Badge, GNotifier& notifier) +{ + m_notifiers.remove(¬ifier); +} diff --git a/LibGUI/GEventLoop.h b/LibGUI/GEventLoop.h index ad5fce26c1..02d1066770 100644 --- a/LibGUI/GEventLoop.h +++ b/LibGUI/GEventLoop.h @@ -1,11 +1,13 @@ #pragma once #include "GEvent.h" +#include #include #include #include class GObject; +class GNotifier; class GWindow; struct GUI_Event; @@ -27,6 +29,9 @@ public: int register_timer(GObject&, int milliseconds, bool should_reload); bool unregister_timer(int timer_id); + void register_notifier(Badge, GNotifier&); + void unregister_notifier(Badge, GNotifier&); + void exit(int); private: @@ -64,4 +69,5 @@ private: }; HashMap> m_timers; + HashTable m_notifiers; }; diff --git a/LibGUI/GNotifier.cpp b/LibGUI/GNotifier.cpp new file mode 100644 index 0000000000..1d5ad6c816 --- /dev/null +++ b/LibGUI/GNotifier.cpp @@ -0,0 +1,15 @@ +#include +#include + +GNotifier::GNotifier(int fd, unsigned event_mask) + : m_fd(fd) + , m_event_mask(event_mask) +{ + GEventLoop::main().register_notifier(Badge(), *this); +} + +GNotifier::~GNotifier() +{ + GEventLoop::main().unregister_notifier(Badge(), *this); +} + diff --git a/LibGUI/GNotifier.h b/LibGUI/GNotifier.h new file mode 100644 index 0000000000..8ea7a1d81f --- /dev/null +++ b/LibGUI/GNotifier.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class GNotifier { +public: + enum Event { + None = 0, + Read = 1, + Write = 2, + Exceptional = 4, + }; + GNotifier(int fd, unsigned event_mask); + ~GNotifier(); + + Function on_ready_to_read; + Function on_ready_to_write; + + int fd() const { return m_fd; } + unsigned event_mask() const { return m_event_mask; } + +private: + int m_fd { -1 }; + unsigned m_event_mask { 0 }; +}; diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index d29052a69c..75f394188a 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -34,16 +34,11 @@ void GWidget::set_relative_rect(const Rect& rect) update(); } -void GWidget::repaint(const Rect& rect) -{ - // FIXME: Implement. -} - void GWidget::event(GEvent& event) { switch (event.type()) { case GEvent::Paint: - m_has_pending_paint_event = false; + m_pending_paint_event_rects.clear(); return handle_paint_event(static_cast(event)); case GEvent::Resize: return handle_resize_event(static_cast(event)); @@ -171,14 +166,21 @@ void GWidget::focusout_event(GEvent&) } void GWidget::update() +{ + update(rect()); +} + +void GWidget::update(const Rect& rect) { auto* w = window(); if (!w) return; - if (m_has_pending_paint_event) - return; - m_has_pending_paint_event = true; - w->update(window_relative_rect()); + for (auto& pending_rect : m_pending_paint_event_rects) { + if (pending_rect.contains(rect)) + return; + } + m_pending_paint_event_rects.append(rect); + w->update(rect.translated(window_relative_rect().location())); } Rect GWidget::window_relative_rect() const diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 8c64635898..ad45fa92d4 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -58,7 +58,7 @@ public: Size size() const { return m_relative_rect.size(); } void update(); - void repaint(const Rect&); + void update(const Rect&); virtual bool accepts_focus() const { return false; } @@ -136,6 +136,7 @@ private: SizePolicy m_vertical_size_policy { SizePolicy::Fill }; Size m_preferred_size; - bool m_has_pending_paint_event { false }; + Vector m_pending_paint_event_rects; + bool m_fill_with_background_color { true }; }; diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index e3a3d27102..f9a76295ce 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -163,13 +163,17 @@ void GWindow::event(GEvent& event) } if (event.is_key_event()) { - if (!m_focused_widget) - return; - return m_focused_widget->event(event); + if (m_focused_widget) + return m_focused_widget->event(event); + if (m_main_widget) + return m_main_widget->event(event); + return; } if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) { m_is_active = event.type() == GEvent::WindowBecameActive; + if (m_main_widget) + m_main_widget->event(event); if (m_focused_widget) m_focused_widget->update(); return; @@ -202,9 +206,18 @@ void GWindow::set_main_widget(GWidget* widget) if (m_main_widget == widget) return; m_main_widget = widget; - m_main_widget->set_relative_rect({ 0, 0, width(), height() }); - if (widget) - widget->set_window(this); + if (m_main_widget) { + auto new_window_rect = rect(); + if (m_main_widget->horizontal_size_policy() == SizePolicy::Fixed) + new_window_rect.set_width(m_main_widget->preferred_size().width()); + if (m_main_widget->vertical_size_policy() == SizePolicy::Fixed) + new_window_rect.set_height(m_main_widget->preferred_size().height()); + set_rect(new_window_rect); + m_main_widget->set_relative_rect({ { }, new_window_rect.size() }); + m_main_widget->set_window(this); + if (m_main_widget->accepts_focus()) + m_main_widget->set_focus(true); + } update(); } diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index 8e0ab3bb69..e7c406d8fe 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -26,6 +26,7 @@ public: int height() const { return rect().height(); } Rect rect() const; + Size size() const { return rect().size(); } void set_rect(const Rect&); void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); } @@ -58,6 +59,8 @@ public: void set_should_exit_app_on_close(bool b) { m_should_exit_app_on_close = b; } private: + virtual const char* class_name() const override { return "GWindow"; } + RetainPtr m_backing; int m_window_id { 0 }; bool m_is_active { false }; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index ee4cbe3e6b..36167d42ad 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -13,6 +13,7 @@ LIBGUI_OBJS = \ GLabel.o \ GListBox.o \ GObject.o \ + GNotifier.o \ GTextBox.o \ GScrollBar.o \ GStatusBar.o \ diff --git a/SharedGraphics/Painter.h b/SharedGraphics/Painter.h index ee997cb0ca..e3e2cb8188 100644 --- a/SharedGraphics/Painter.h +++ b/SharedGraphics/Painter.h @@ -52,6 +52,8 @@ public: void translate(int dx, int dy) { m_translation.move_by(dx, dy); } void translate(const Point& delta) { m_translation.move_by(delta); } + GraphicsBitmap* target() { return m_target.ptr(); } + private: void set_pixel_with_draw_op(dword& pixel, const Color&); void fill_rect_with_draw_op(const Rect&, Color); diff --git a/SharedGraphics/Rect.h b/SharedGraphics/Rect.h index 9f6f89cefd..d7ed95dcd1 100644 --- a/SharedGraphics/Rect.h +++ b/SharedGraphics/Rect.h @@ -99,6 +99,13 @@ public: return rect; } + Rect translated(const Point& delta) const + { + Rect rect = *this; + rect.move_by(delta); + return rect; + } + bool contains(int x, int y) const { return x >= m_location.x() && x <= right() && y >= m_location.y() && y <= bottom();