I'm starting using Susy and there's something I'd like to accomplish but I don't know how, although I'm reading Susy's documentation and online tutorials.
I have this html:
<div class="page">
<nav>nav</nav>
<div class="main">
<div class="summary">
summary
</div>
<div class="content">
content
</div>
</div>
<footer>footer</footer>
</div>
and these Susy settings:
$total-columns: 12;
$column-width: 4em;
$gutter-width: 1em;
$grid-padding: $gutter-width;
$show-grid-backgrounds: true;
#include border-box-sizing;
// breakpoint variables
$M: 30em;
$L: 50em;
and this scss:
.page {
#include container;
#include susy-grid-background;
nav {
#include at-breakpoint($M) {
#include span-columns(2,12);
}
}
.main {
#include at-breakpoint($M) {
#include span-columns(10 omega,12);
}
.summary {
#include at-breakpoint($L) {
#include span-columns(2 omega,10);
}
}
.content {
#include at-breakpoint($L) {
#include span-columns(8,10);
}
}
}
footer {clear: both;}
That works as expected, the content is totally fluid with a max-width. However, I would like the same behaviour but starting with a 4 column layout and then changing to 8 columns and then 12 columns.
I do it like this:
$total-columns: 4;
$column-width: 4em;
$gutter-width: 1em;
$grid-padding: $gutter-width;
$show-grid-backgrounds: true;
#include border-box-sizing;
// breakpoint variables
$M: 30em;
$L: 50em;
and the scss:
.page {
#include container;
#include susy-grid-background;
// Now create a media-query breakpoint at the min-width of 30em and use a larger grid and modify the layout
#include at-breakpoint($M 8) {
// This will create a new container with a total of 8 columns
#include container;
#include susy-grid-background;
// Now modify the inner elements to their new home
nav { #include span-columns(2,8); }
.main { #include span-columns(6 omega,8); }
}
#include at-breakpoint($L 12) {
// This will create a new container with a total of 12 columns
#include container;
#include susy-grid-background;
// Now modify the inner elements to their new home
nav { #include span-columns(2,12); }
.main {
#include span-columns(10 omega,12);
.content {
#include span-columns(8,10);
}
.summary {
#include span-columns(2 omega,10);
}
}
}
footer {clear: both;}
}
This also works ok, but I can't make all the layouts liquid as in the first example. For example, at 450px wide the 4 column layout doesn't fill the viewport. The same happens at 768px, the 8 columns don't fill the viewport. I'd like the layout to always fill the available width, as in the first example, as well as change columns according to defined breakpoints.
Is that the normal Susy behaviour or is it possible to do it in another way?
Excuse me if this is a newbie question, but I'm just at the beginning and I'd like to make things clear before using Susy on real projects.
Thanks.
Here is how I handle responsive grids, maybe it will help you.
I have a few variables I define for each breakpoint (which I like to name by the number of columns) for instance:
// 6 Columns -------------------------------------------------------------------
$six-gut-width : .5rem;
$six-padding : 0;
$six-width : 6 * $column-width + 5 * $six-gut-width + $six-padding * 2;
// 8 Columns -------------------------------------------------------------------
$eight-width : 8 * $column-width + 7 * $gutter-width + $grid-padding * 2;
This way I have both the actual width and number of columns to use in my at-breakpoint calls.
Then my breakpoints break down like this:
#include at-breakpoint($six-width 6 $eight-width) {
// Breakpoint specific scss
.page { set-container-width(6); }
}
I like to keep the breakpoint specific stuff in it's own partial in a breakpoints directory (but you don't have to) i.e: breakpoints/_6-cols.scss, breakpoints/_8-cols.scss etc.
If you want to cascade to a larger breakpoint then leave off the third parameter on at-breakpoint(), or set it to something higher than the next level. Also be sure you're setting set-container-width instead of container in your breakpoint declarations. Check out set-container-width on Susy docs
Hope this helps you. Best of luck.
Related
I am seeing a problem while restoring QMainWindow state having QCombobox in floating toolbar. After restoring floating toolbar, my QCombobox is not able to get focus until i click on toolbar handle and move it.
Following is gif showing problem, Using QT 5.13.
File floating_toolbar.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = floating_toolbar
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
File : main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
File : mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
void closeEvent(QCloseEvent *event);
void readSettings();
bool eventFilter(QObject* xObj, QEvent* xEvent);
~MainWindow();
public slots:
void mCheck();
};
#endif // MAINWINDOW_H
File : mainwindow.cpp
#include "mainwindow.h"
#include <QToolBar>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLayout>
#include <QSettings>
#include <QEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QToolBar* lToolbar = new QToolBar(this);
QComboBox* lComobox = new QComboBox(this);
lComobox->setEditable(true);
lToolbar->setWindowTitle("MyToolBar");
lToolbar->setObjectName("NiceBaby");
lToolbar->addWidget(lComobox);
//lToolbar->addAction("check", lComobox, SLOT(clearEditText()));
addToolBar(lToolbar);
lToolbar->installEventFilter(this);
readSettings();
}
void MainWindow::mCheck()
{
}
void MainWindow::closeEvent(QCloseEvent *event)
{
QSettings settings("MyCompany", "MyApp");
settings.setValue("windowState", saveState());
QMainWindow::closeEvent(event);
}
void MainWindow::readSettings()
{
QSettings settings("MyCompany", "MyApp");
restoreState(settings.value("windowState").toByteArray());
}
MainWindow::~MainWindow()
{
}
bool MainWindow::eventFilter(QObject* xObj, QEvent* xEvent)
{
//qDebug()<<xEvent->type();
return QMainWindow::eventFilter(xObj, xEvent);
}
OK, a workaround is to reset the window flags on the toolbar when it is first shown and is floating. I tracked this down by seeing what happens once a toolbar is dropped after being dragged (but not plugged into main window). (It calls setWindowState() and all that does in this situation is hide the toolbar, call updateWindowFlags(), and show it again).
This could be handled from the QMainWindow::showEvent() or from the eventFilter installed onto the QToolBar. I think it's simpler from the former.
UPDATE: This problem actually happens whenever the toolbar is first shown even if not at app startup, e.g. from the toggle view menu by the user once app starts. I updated the code below to fix that issue as well. And see notes below about another issue with minimizing the main window.
I added this to the MainWindow class from the MCVE:
protected:
void showEvent(QShowEvent *e) override {
QMainWindow::showEvent(e);
#ifdef Q_OS_LINUX
if (lToolbar->isFloating()
// remove the next condition and the toolsbar will get hidden the 2nd time main window is minimized.
&& lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint)
) {
const bool vis = !lToolbar->isHidden();
qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
lToolbar->hide();
lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
lToolbar->show();
#endif
}
QToolBar* lToolbar; // Use this in MainWindow constructor to save the instance pointer.
I also noticed another issue with the initially-floating toolbar. When the main window is minimized, the toolbar doesn't get hidden but stays where it was on the screen. Regardless of what is in the toolbar (eg. no combo box, just QActions). This workaround could also sort-of address that issue (see code comment), but only the 2nd time the window is minimized. Needs a better workaround for the first minimize.
Can others confirm this? Potentially a larger issue than the editable combo and I'd be surprised if no one noticed before.
I guess this should be filed as a Qt bug either way.
UPDATE2: This version also fixes the minimize issue. I guess something happens after the QMainWindow::showEvent() that changes how the toolbar behaves. Which explains why the above workaround works only after the 1st minimize. So scheduling the toolbar "fix" for later works around that also.
class MainWindow : public QMainWindow
{
...
#ifdef Q_OS_LINUX
protected:
void showEvent(QShowEvent *e) override
{
QMainWindow::showEvent(e);
if (lToolbar->isFloating() && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
// QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
// If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
//adjustToolbar();
}
}
private slots:
void adjustToolbar() const
{
const bool vis = !lToolbar->isHidden();
qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
lToolbar->hide();
lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
lToolbar->show();
}
#endif
private:
QToolBar* lToolbar;
};
ADDED: A QToolBar subclass which applies the workaround on its own, nothing special needed in the QMainWindow. The minimize fix still only works when the adjustToolbar() function is queued or if restoreState() is only called after show() (see code comments).
class ToolBar : public QToolBar
{
Q_OBJECT
public:
using QToolBar::QToolBar;
#ifdef Q_OS_LINUX
protected:
void showEvent(QShowEvent *e) override
{
QToolBar::showEvent(e);
if (isFloating() && windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
// QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
// If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
//adjustToolbar();
}
}
private slots:
void adjustToolbar()
{
const bool vis = !isHidden();
hide();
setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
show();
}
#endif
};
UPDATE3: The minimizing issue also exists with floating QDockWidget if the QMainWindow state is restored before it is shown. In fact with "older" Qt versions the floating widget doesn't show up at all (doesn't with <= 5.9.5 but does with >= 5.12.4, don't have anything in between to try ATM). So the proper approach is to show() the main window first and then restoreState(). Unfortunately this doesn't seem to work for QToolBar.
UPDATE4: Filed as QTBUG-78293
It seems to work normally on macOS:
I have 3 files in my project, main.cpp, Globals.h & Globals.cpp:
main.cpp
#include "Globals.h"
int main()
{
if (setup())return 1;//If setup fails terminate the program
CenterOrigin(playerSprite);
while (window.isOpen())
{
deltaTime = deltaClock.restart().asSeconds();
while (window.pollEvent(ev))
{
if (ev.type == sf::Event::Closed)window.close();
}
float rotSpeed = 5;
window.clear(sf::Color(12, 14, 12, 0.9 * 255));
window.display();
}
return 0;
}
Globals.h
#pragma once
#include <iostream>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#define TITLE "Belty McBelth"
#define WIDTH 1280
#define HEIGHT 720
sf::RenderWindow window;
sf::Font font;
sf::Event ev;
sf::Texture playerTex;
sf::Sprite playerSprite;
sf::Texture tieTex;
sf::Sprite tieSprite;
sf::Clock deltaClock;
float deltaTime; //Time since last frame in seconds
int setup();
void CenterOrigin(sf::Sprite & t);
Globals.cpp
#include "Globals.h"
int setup()
{
window.create(sf::VideoMode(WIDTH, HEIGHT), "Belty McBelth", sf::Style::Close);
if (!font.loadFromFile("../comic.ttf"))return 1;
if (!playerTex.loadFromFile("../PLAYER.png"))return 1;
playerSprite = sf::Sprite(playerTex);
std::cout << "Setup completed without errors \n";
return 0;
}
void CenterOrigin(sf::Sprite & t)
{
sf::FloatRect d = t.getLocalBounds();
t.setOrigin(d.width / 2, d.height / 2);
}
Whenever I try to build this project, I get a plethora of LNK2005 errors, one for each variable that is declared in the globals header file. I have searched for a solution to this problem, however I am not able to find one. I have ensured that there are no definitions in the globals.h file, however, I am still unable to build the project.
This problem solves itself if I move all the definitions into the .h file, however, through the microsoft page for the lnk2005 error, I have found that this is only because there is only one file that includes the globals header file.
Sidenote, is there any better way of handling global variables/functions? Extern is an option, but it gets too cumbersome when you have a lot of global variables.
The following code places my sidebar-first one column off the left of the screen.
.has-sidebar-first {
.l-content {
#include span-columns(15 omega, 16); // Span 15 out of 16 columns.
#include push(1, 16); // Push element by adding 1 out of 16 columns of left margin.
}
.l-region--sidebar-first {
#include span-columns(1, 16); // Span 1 out of 16 columns.
#include pull(1, 16); // Pull element by adding 15 out of 16 columns of negative left margin.
}
}
The sidebar-first should take up the first column and content should take up the next 15.
I have had pull set 1 through to 16 but it is either out of place or disappears entirely.
Any suggestion?
Update1: Here is the full scss layout(including the suggestion from Eric Meyer (the man himself!) places the sidebar-first further off page to the left. It appears to be offpage by the same width as the l-content .
#import "susy";
// Susy Variables
// Set consistent vertical and horizontal spacing units.
$vert-spacing-unit: 20px;
$horz-spacing-unit: 1em;
// Define Susy grid variables mobile first.
$total-columns: 4;
$column-width: 4em;
$gutter-width: $horz-spacing-unit;
$grid-padding: 5px;
$container-style: magic;
$container-width: 1200px;
// Susy Media Layouts #see http://susy.oddbird.net/guides/reference/#ref-media-layouts
$tab: 44em 12; // At 44em use 12 columns.
$desk: 70em 16; // At 70em use 16 columns.
.l-header,
.l-main,
.l-footer {
#include container; // Define these elements as the grid containers.
margin-bottom: $vert-spacing-unit;
}
.l-region--highlighted,
.l-region--help,
.l-region--sidebar-first,
.l-region--sidebar-second {
margin-bottom: $vert-spacing-unit;
}
#include at-breakpoint($tab) { // At a given Susy Media Layout, use a given amount of columns.
.l-header,
.l-main,
.l-footer {
#include set-container-width; // Reset only the container width (elements have already been declared as containers).
}
.l-branding {
#include span-columns(4, 12); // Span 4 out of 12 columns.
}
.l-region--header{
#include span-columns(8 omega, 12); // Span the last (omega) 8 columns of 12.
}
.l-region--navigation {
clear: both;
}
.has-sidebar-first,
.has-sidebar-second,
.has-two-sidebars {
.l-content {
#include span-columns(7, 12); // Span 7 out of 12 columns.
#include push(1, 12); // Push element by adding 1 out of 12 columns of left margin.
}
.l-region--sidebar-first, {
#include span-columns(1, 12); // Span the 1 columns of 12.
}
.l-region--sidebar-second {
#include span-columns(4 omega, 12); // Span the last (omega) 4 columns of 12.
}
.l-region--sidebar-first {
#include pull(8, 12); // Pull element by adding 8 out of 12 columns of negative left margin.
}
.l-region--sidebar-second {
clear: right;
}
}
}
#include at-breakpoint($desk) {
.l-header,
.l-main,
.l-footer {
#include set-container-width; // Reset only the container width (elements have already been declared as containers).
}
.l-branding {
#include span-columns(6, 16); // Span 6 out of 16 columns.
}
.l-region--header{
#include span-columns(10 omega, 16); // Span the last (omega) 10 columns of 16.
}
.has-sidebar-first {
.l-content {
#include span-columns(15 omega, 16); // Span 15 out of 16 columns.
}
.l-region--sidebar-first {
#include span-columns(1, 16); // Span 1 out of 16 columns.
}
}
.has-sidebar-second {
.l-content {
#include span-columns(12, 16); // Span 12 out of 16 columns.
}
.l-region--sidebar-second {
#include span-columns(4 omega, 16); // Span the last (omega) 4 columns of 16.
clear: none;
}
}
.has-two-sidebars {
.l-content {
#include span-columns(10, 16); // Span 10 out of 16 columns.
#include push(1, 16); // Push element by adding 1 out of 16 columns of left margin.
}
.l-region--sidebar-first {
#include span-columns(1, 16); // Span 1 out of 16 columns.
}
.l-region--sidebar-second {
#include span-columns(5, 16); // Span 5 out of 16 columns.
}
.l-region--sidebar-first {
#include pull(11, 16); // Pull element by adding 11 out of 16 columns of negative left margin.
}
.l-region--sidebar-second {
#include omega; // This element spans the last (omega) column.
clear: none;
}
}
}
.has-two-sidebars is working as desired. I am only hoping to fix .has-sidebar-first when #include at-breakpoint($desk) . If there is something inherently wrong with how it is set up then I will have to change the lot but I am hoping to simply change the the layout when viewed on a desktop where the is no sidebar second.
Thanks
Update 2
Following the suggestion to add margin-left: 0; here is it added.
.has-sidebar-first {
.l-content {
#include span-columns(15 omega, 16); // Span 15 out of 16 columns.
}
.l-region--sidebar-first {
#include span-columns(1, 16); // Span 1 out of 16 columns.
margin-left: 0;
}
}
While this now aligns the 'side-first' to the correct column, it appears below the content, as per the picture:
The rest of the code is the same. The two sidebar option still displays correctly.
Any suggestions?
Solution:
As per Eric's suggestion I needed to clear and previously declared push and pulls. So the correct code is:
.has-sidebar-first {
.l-content {
#include span-columns(15 omega, 16); // Span 15 out of 16 columns.
margin-left: 0;
}
.l-region--sidebar-first {
#include span-columns(1, 16); // Span 1 out of 16 columns.
margin-left: 0;
}
Thanks
Get rid of both the push and the pull. Neither one is needed. Your omega item is floated right, and the other item is floated left, so both will fall perfectly into place without needing any push/pull help.
update:
You have a pull set on .l-region--sidebar-first at one of the smaller breakpoints, which is still being applied at the larger breakpoint. You just need to set margin-left to 0 at the $desk breakpoint.
I'm trying to figure out how to use a different grid setting based on a breakpoint but can't get it to work. Here's my code. I'm using the respond-to mixin for media queries.
$breakpoints: 'screenSmall' 480px 768px,
'screenMedium' 768px 1279px,
'screenXlarge' 1441px;
$total-columns: 4;
$column-width: 5em;
$gutter-width: 1em;
$grid-padding: $gutter-width;
[role="main"] {
#include container($total-columns);
background: #aaa;
#include susy-grid-background;
}
#include respond-to('screenSmall')
#include with-grid-settings(8,4em,1.5em,1em) {
[role="main"] { #include container; }
};
}
#include respond-to('screenMedium')
#include with-grid-settings(12,4em,1.5em,1em) {
[role="main"] { #include container; }
};
}
What exactly isn't working about it? Is there no change at all for the different breakpoints, or simply no change to the background grid image? I'm guessing the latter. If you want the background to respond to new settings, you will need to re-declare the background with those settings:
#include respond-to('screenSmall')
#include with-grid-settings(8,4em,1.5em,1em) {
[role="main"] {
#include container;
#include susy-grid-background;
}
};
}
#include respond-to('screenMedium')
#include with-grid-settings(12,4em,1.5em,1em) {
[role="main"] {
#include container;
#include susy-grid-background;
}
};
}
Of course, I think you could do it more easily with at-breakpoint rather than respond-to. But that's a different question. :)
I ran into troubles trying to create a gnome-panel applet with gtkmm. I dealt with most of them, but I'm now kind of blocked.
Quick summary : I tried libpanelappletmm, but every program (even the examples supplied in the source code) segfaults when I try to add the applet in my panel.
So I now use the C library (libpanel-applet). First I looked for a way to wrap the PanelApplet Gobject in a gtkmm C++-object, for example a Gtk::EventBox (PanelApplet inherits from GtkEventBox). I tried to cast it, but Glibmm kept throwing a warning ("Failed to wrap object 'PanelApplet'").
So I created a class "Info", inheriting from Gtk::HBox. In my main.cpp file I declare an instance of it, get the underlying GTK object (gobj method), and use the GTK+ functions to add it into the PanelApplet.
Here's my main.cpp.
#include <iostream>
#include <gtkmm.h>
#include <panel-applet.h>
#include "Info.hpp"
static void manage_timeboxes(BonoboUIComponent *uic, void *applet, const char* data) {
std::cout << "manage" << std::endl;
}
static gboolean getApplet(PanelApplet *applet, const gchar *iid, gpointer data) {
/*
if(iid != "OAFIID:TimeboxingApplet")
return false;
*/
Glib::init();
Gtk::Widget* content = new Info();
gtk_container_add(GTK_CONTAINER(applet), content->gobj());
static const char menu_xml[] =
"<popup name=\"button3\">\n"
" <menuitem name=\"Manage\" "
" verb=\"manage_timeboxes\" "
" _label=\"_GĂ©rer l'emploi du temps\"\n"
" pixtype=\"stock\" "
" pixname=\"gtk-properties\"/>\n"
"</popup>\n";
static const BonoboUIVerb linked_verbs[] = {
BONOBO_UI_VERB ("manage_timeboxes", manage_timeboxes),
BONOBO_UI_VERB_END
};
panel_applet_setup_menu(applet, menu_xml, linked_verbs, data);
gtk_widget_show_all(GTK_WIDGET(applet));
return true;
}
PANEL_APPLET_BONOBO_FACTORY (
"OAFIID:TimeboxingApplet_Factory",
PANEL_TYPE_APPLET,
"Timeboxing",
"0.0",
getApplet,
NULL)
It works fine if I add labels or buttons in my Info object.
But then I tried to add an icon.
My first try was adding a Gtk::Image as a property of Info.
Info.hpp
#ifndef TIMEBOXING_INFO_H
#define TIMEBOXING_INFO_H
#include <gtkmm/box.h>
#include <gtkmm/image.h>
#include <gtkmm/label.h>
class Info : public Gtk::HBox {
public:
Info();
virtual ~Info(){};
protected:
Gtk::Image icon;
Gtk::Label info;
};
#endif
Info.cpp
#include "Info.hpp"
#include <gtkmm/image.h>
#include <gtkmm/label.h>
Info::Info() : icon("/home/bastien/programmation/timeboxing-applet/icons/clock-24.png"), info("<b>En cours</b>") {
info.set_use_markup();
pack_start(icon);
pack_start(info);
show_all_children();
}
When I try to add the applet, I get this error and the program aborts :
glibmm:ERROR:objectbase.cc:78:void Glib::ObjectBase::initialize(GObject*): assertion failed: (gobject_ == castitem)
I commented "Gtk::Image icon" from Info.hpp, and I modified my constructor like this :
Info::Info() : info("<b>En cours</b>") {
info.set_use_markup();
Gtk::Image icon("/home/bastien/programmation/timeboxing-applet/icons/clock-24.png");
pack_start(icon);
pack_start(info);
show_all_children();
}
I'm not getting the Glibmm error anymore, but the image isn't displayed. I tried with another file, with an icon from the stock, and even with a Gdk::Pixbuf.
Thank you in advance !
Well, strangely enough, it works if I create a pointer to Gtk::Image.
If anyone has an explanation, it would be great !
Edit : apparently, I had to call Gtk::Main::init_gtkmm_internals. My wrapping troubles went away. I can wrap PanelApplet too, but if I use the resulting Gtk::EventBox* it doesn't display anything.