static void grid_placement ( DeepinWorkspaceOverview* self, MonitorData* md, MetaRectangle area, gboolean closest) { DeepinWorkspaceOverviewPrivate* priv = self->priv; GPtrArray* clones = md->clones; if (!clones || clones->len == 0) return; int window_count = clones->len; int columns = (int)ceil (sqrt (window_count)); int rows = (int)ceil (window_count / (double)columns); // Assign slots int slot_width = area.width / columns; int slot_height = area.height / rows; GList* windows = NULL; for (int i = 0; i < clones->len; i++) { MetaDeepinClonedWidget* clone = g_ptr_array_index(clones, i); TilableWindow* tw = g_new0(TilableWindow, 1); tw->id = clone; MetaWindow* win = meta_deepin_cloned_widget_get_window(clone); meta_window_get_input_rect(win, &tw->rect); windows = g_list_append(windows, tw); } TilableWindow* taken_slots[rows * columns]; memset(taken_slots, 0, sizeof taken_slots); if (closest) { // Assign each window to the closest available slot. // precalculate all slot centers GdkPoint slot_centers[rows * columns]; memset(slot_centers, 0, sizeof slot_centers); for (int x = 0; x < columns; x++) { for (int y = 0; y < rows; y++) { slot_centers[x + y * columns] = (GdkPoint){ area.x + slot_width * x + slot_width / 2, area.y + slot_height * y + slot_height / 2 }; } } GList* tmplist = g_list_copy(windows); while (g_list_length(tmplist) > 0) { GList* link = g_list_nth(tmplist, 0); TilableWindow* window = (TilableWindow*)link->data; MetaRectangle rect = window->rect; int slot_candidate = -1; int slot_candidate_distance = INT_MAX; GdkPoint pos = rect_center (rect); // all slots for (int i = 0; i < columns * rows; i++) { if (i > window_count - 1) break; int dist = squared_distance (pos, slot_centers[i]); if (dist < slot_candidate_distance) { // window is interested in this slot TilableWindow* occupier = taken_slots[i]; if (occupier == window) continue; if (occupier == NULL || dist < squared_distance (rect_center (occupier->rect), slot_centers[i])) { // either nobody lives here, or we're better - takeover the slot if it's our best slot_candidate = i; slot_candidate_distance = dist; } } } if (slot_candidate == -1) continue; if (taken_slots[slot_candidate] != NULL) tmplist = g_list_prepend(tmplist, taken_slots[slot_candidate]); tmplist = g_list_remove_link(tmplist, link); taken_slots[slot_candidate] = window; g_list_free(link); } g_list_free(tmplist); } else { // Assign each window as the origin order. for (int i = 0; i < clones->len; i++) { GList* link = g_list_nth (windows, i); taken_slots[i] = (TilableWindow*)link->data; } } // see how many windows we have on the last row int left_over = (int)window_count - columns * (rows - 1); for (int slot = 0; slot < columns * rows; slot++) { TilableWindow* window = taken_slots[slot]; // some slots might be empty if (window == NULL) continue; MetaRectangle rect = window->rect; // Work out where the slot is MetaRectangle target = { area.x + (slot % columns) * slot_width, area.y + (slot / columns) * slot_height, slot_width, slot_height }; target = rect_adjusted (target, 10, 10, -10, -10); float scale; if (target.width / (double)rect.width < target.height / (double)rect.height) { // Center vertically scale = target.width / (float)rect.width; target.y += (target.height - (int)(rect.height * scale)) / 2; target.height = (int)floorf (rect.height * scale); } else { // Center horizontally scale = target.height / (float)rect.height; target.x += (target.width - (int)(rect.width * scale)) / 2; target.width = (int)floorf (rect.width * scale); } // Don't scale the windows too much if (scale > 1.0) { scale = 1.0f; target = (MetaRectangle){ rect_center (target).x - (int)floorf (rect.width * scale) / 2, rect_center (target).y - (int)floorf (rect.height * scale) / 2, (int)floorf (scale * rect.width), (int)floorf (scale * rect.height) }; } // put the last row in the center, if necessary if (left_over != columns && slot >= columns * (rows - 1)) target.x += (columns - left_over) * slot_width / 2; place_window(self, window->id, target); } g_list_free_full(windows, g_free); }
static void natural_placement (MosesOverview* self, MetaRectangle area) { g_debug("%s: geom: %d,%d,%d,%d", __func__, area.x, area.y, area.width, area.height); MosesOverviewPrivate* priv = self->priv; GPtrArray* clones = priv->clones; MetaRectangle bounds = {area.x, area.y, area.width, area.height}; int direction = 0; int* directions = g_malloc(sizeof(int)*clones->len); MetaRectangle* rects = g_malloc(sizeof(MetaRectangle)*clones->len); for (int i = 0; i < clones->len; i++) { // save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y] MetaRectangle rect; ClutterActor* clone = g_ptr_array_index(clones, i); MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone))); MetaWindow* win = meta_window_actor_get_meta_window(actor); meta_window_get_frame_rect(win, &rect); rect = rect_adjusted(rect, -GAPS, -GAPS, GAPS, GAPS); rects[i] = rect; g_debug("%s: frame: %d,%d,%d,%d", __func__, rect.x, rect.y, rect.width, rect.height); meta_rectangle_union(&bounds, &rect, &bounds); // This is used when the window is on the edge of the screen to try to use as much screen real estate as possible. directions[i] = direction; direction++; if (direction == 4) direction = 0; } int loop_counter = 0; gboolean overlap = FALSE; do { overlap = FALSE; for (int i = 0; i < clones->len; i++) { for (int j = 0; j < clones->len; j++) { if (i == j) continue; MetaRectangle rect = rects[i]; MetaRectangle comp = rects[j]; if (!meta_rectangle_overlap(&rect, &comp)) continue; loop_counter ++; overlap = TRUE; // Determine pushing direction GdkPoint i_center = rect_center (rect); GdkPoint j_center = rect_center (comp); GdkPoint diff = {j_center.x - i_center.x, j_center.y - i_center.y}; // Prevent dividing by zero and non-movement if (diff.x == 0 && diff.y == 0) diff.x = 1; // Approximate a vector of between 10px and 20px in magnitude in the same direction float length = sqrtf (diff.x * diff.x + diff.y * diff.y); diff.x = (int)floorf (diff.x * ACCURACY / length); diff.y = (int)floorf (diff.y * ACCURACY / length); // Move both windows apart rect.x += -diff.x; rect.y += -diff.y; comp.x += diff.x; comp.y += diff.y; // Try to keep the bounding rect the same aspect as the screen so that more // screen real estate is utilised. We do this by splitting the screen into nine // equal sections, if the window center is in any of the corner sections pull the // window towards the outer corner. If it is in any of the other edge sections // alternate between each corner on that edge. We don't want to determine it // randomly as it will not produce consistant locations when using the filter. // Only move one window so we don't cause large amounts of unnecessary zooming // in some situations. We need to do this even when expanding later just in case // all windows are the same size. // (We are using an old bounding rect for this, hopefully it doesn't matter) int x_section = (int)roundf ((rect.x - bounds.x) / (bounds.width / 3.0f)); int y_section = (int)roundf ((comp.y - bounds.y) / (bounds.height / 3.0f)); i_center = rect_center (rect); diff.x = 0; diff.y = 0; if (x_section != 1 || y_section != 1) { // Remove this if you want the center to pull as well if (x_section == 1) x_section = (directions[i] / 2 == 1 ? 2 : 0); if (y_section == 1) y_section = (directions[i] % 2 == 1 ? 2 : 0); } if (x_section == 0 && y_section == 0) { diff.x = bounds.x - i_center.x; diff.y = bounds.y - i_center.y; } if (x_section == 2 && y_section == 0) { diff.x = bounds.x + bounds.width - i_center.x; diff.y = bounds.y - i_center.y; } if (x_section == 2 && y_section == 2) { diff.x = bounds.x + bounds.width - i_center.x; diff.y = bounds.y + bounds.height - i_center.y; } if (x_section == 0 && y_section == 2) { diff.x = bounds.x - i_center.x; diff.y = bounds.y + bounds.height - i_center.y; } if (diff.x != 0 || diff.y != 0) { length = sqrtf (diff.x * diff.x + diff.y * diff.y); diff.x *= (int)floorf (ACCURACY / length / 2.0f); diff.y *= (int)floorf (ACCURACY / length / 2.0f); rect.x += diff.x; rect.y += diff.y; } // Update bounding rect meta_rectangle_union(&bounds, &rect, &bounds); meta_rectangle_union(&bounds, &comp, &bounds); //we took copies from the rects from our list so we need to reassign them rects[i] = rect; rects[j] = comp; } } } while (overlap && loop_counter < MAX_TRANSLATIONS); // Work out scaling by getting the most top-left and most bottom-right window coords. float scale = fminf (fminf (area.width / (float)bounds.width, area.height / (float)bounds.height), 1.0f); // Make bounding rect fill the screen size for later steps bounds.x = (int)floorf (bounds.x - (area.width - bounds.width * scale) / 2); bounds.y = (int)floorf (bounds.y - (area.height - bounds.height * scale) / 2); bounds.width = (int)floorf (area.width / scale); bounds.height = (int)floorf (area.height / scale); // Move all windows back onto the screen and set their scale int index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; rects[index] = (MetaRectangle){ (int)floorf ((rect.x - bounds.x) * scale + area.x), (int)floorf ((rect.y - bounds.y) * scale + area.y), (int)floorf (rect.width * scale), (int)floorf (rect.height * scale) }; } // fill gaps by enlarging windows gboolean moved = FALSE; MetaRectangle border = area; do { moved = FALSE; index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; int width_diff = ACCURACY; int height_diff = (int)floorf ((((rect.width + width_diff) - rect.height) / (float)rect.width) * rect.height); int x_diff = width_diff / 2; int y_diff = height_diff / 2; //top right MetaRectangle old = rect; rect = (MetaRectangle){ rect.x + x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff }; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //bottom right old = rect; rect = (MetaRectangle){rect.x + x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //bottom left old = rect; rect = (MetaRectangle){rect.x - x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //top left old = rect; rect = (MetaRectangle){rect.x - x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; rects[index] = rect; } } while (moved); index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; ClutterActor* clone = g_ptr_array_index(clones, index); MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone))); MetaWindow* window = meta_window_actor_get_meta_window(actor); MetaRectangle window_rect; meta_window_get_frame_rect(window, &window_rect); rect = rect_adjusted(rect, GAPS, GAPS, -GAPS, -GAPS); scale = rect.width / (float)window_rect.width; if (scale > 2.0 || (scale > 1.0 && (window_rect.width > 300 || window_rect.height > 300))) { scale = (window_rect.width > 300 || window_rect.height > 300) ? 1.0f : 2.0f; rect = (MetaRectangle){rect_center (rect).x - (int)floorf (window_rect.width * scale) / 2, rect_center (rect).y - (int)floorf (window_rect.height * scale) / 2, (int)floorf (window_rect.width * scale), (int)floorf (window_rect.height * scale)}; } place_window(self, clone, rect); } g_free(directions); g_free(rects); }