Esempio n. 1
0
void SetWindowSizeAndFitToScreen(wxTopLevelWindow* tlw, wxPoint pos, wxSize size,
                                 wxSize default_size)
{
  if (tlw->IsMaximized())
    return;

  // NOTE: Positions can be negative and still be valid. Coordinates are relative to the
  //   primary monitor so if the primary monitor is in the middle then (-1000, 10) is a
  //   valid position on the monitor to the left of the primary. (This does not apply to
  //   sizes obviously)
  wxRect screen_geometry;
  wxRect window_geometry{pos, size};

  if (wxDisplay::GetCount() > 1)
    screen_geometry = GetVirtualScreenGeometry();
  else
    screen_geometry = wxDisplay(0).GetClientArea();

  // Initialize the default size if it is wxDefaultSize or otherwise negative.
  default_size.DecTo(screen_geometry.GetSize());
  default_size.IncTo(tlw->GetMinSize());
  if (!default_size.IsFullySpecified())
    default_size.SetDefaults(wxDisplay(0).GetClientArea().GetSize() / 2);

  // If the position we're given doesn't make sense then go with the current position.
  // (Assuming the window was created with wxDefaultPosition then this should be reasonable)
  if (pos.x - screen_geometry.GetLeft() < -1000 || pos.y - screen_geometry.GetTop() < -1000 ||
      pos.x - screen_geometry.GetRight() > 1000 || pos.y - screen_geometry.GetBottom() > 1000)
  {
    window_geometry.SetPosition(tlw->GetPosition());
  }

  // If the window is bigger than all monitors combined, or negative (uninitialized) then reset it.
  if (window_geometry.IsEmpty() || window_geometry.GetWidth() > screen_geometry.GetWidth() ||
      window_geometry.GetHeight() > screen_geometry.GetHeight())
  {
    window_geometry.SetSize(default_size);
  }

  // Check if the window entirely lives on a single monitor without spanning.
  // If the window does not span multiple screens then we should constrain it within that
  // single monitor instead of the entire virtual desktop space.
  // The benefit to doing this is that we can account for the OS X menu bar and Windows task
  // bar which are treated as invisible when only looking at the virtual desktop instead of
  // an individual screen.
  if (wxDisplay::GetCount() > 1)
  {
    // SPECIAL CASE: If the window is entirely outside the visible area of the desktop then we
    //   put it back on the primary (zero) monitor.
    wxRect monitor_intersection{window_geometry};
    int the_monitor = 0;
    if (!monitor_intersection.Intersect(screen_geometry).IsEmpty())
    {
      std::array<int, 4> monitors{{wxDisplay::GetFromPoint(monitor_intersection.GetTopLeft()),
                                   wxDisplay::GetFromPoint(monitor_intersection.GetTopRight()),
                                   wxDisplay::GetFromPoint(monitor_intersection.GetBottomLeft()),
                                   wxDisplay::GetFromPoint(monitor_intersection.GetBottomRight())}};
      the_monitor = wxNOT_FOUND;
      bool intersected = false;
      for (int one_monitor : monitors)
      {
        if (one_monitor == the_monitor || one_monitor == wxNOT_FOUND)
          continue;
        if (the_monitor != wxNOT_FOUND)
        {
          // The window is spanning multiple screens.
          the_monitor = wxNOT_FOUND;
          break;
        }
        the_monitor = one_monitor;
        intersected = true;
      }
      // If we get wxNOT_FOUND for all corners then there are holes in the virtual desktop and the
      // entire window is lost in one. (e.g. 3 monitors in an 'L', window in top-right)
      if (!intersected)
        the_monitor = 0;
    }
    if (the_monitor != wxNOT_FOUND)
    {
      // We'll only use the client area of this monitor if the window will actually fit.
      // (It may not fit if the window is spilling off the edge so it isn't entirely visible)
      wxRect client_area{wxDisplay(the_monitor).GetClientArea()};
      if (client_area.GetWidth() >= window_geometry.GetWidth() &&
          client_area.GetHeight() >= window_geometry.GetHeight())
      {
        screen_geometry = client_area;
      }
    }
  }

  // The window SHOULD be small enough to fit on the screen, but it might be spilling off an edge
  // so we'll snap it to the nearest edge as necessary.
  if (!screen_geometry.Contains(window_geometry))
  {
    // NOTE: The order is important here, if the window *is* too big to fit then it will snap to
    //   the top-left corner.
    int spill_x = std::max(0, window_geometry.GetRight() - screen_geometry.GetRight());
    int spill_y = std::max(0, window_geometry.GetBottom() - screen_geometry.GetBottom());
    window_geometry.Offset(-spill_x, -spill_y);
    if (window_geometry.GetTop() < screen_geometry.GetTop())
      window_geometry.SetTop(screen_geometry.GetTop());
    if (window_geometry.GetLeft() < screen_geometry.GetLeft())
      window_geometry.SetLeft(screen_geometry.GetLeft());
  }

  tlw->SetSize(window_geometry, wxSIZE_ALLOW_MINUS_ONE);
}