mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
Fixes for position: sticky, absolute and fixed.
This commit is contained in:
621
bin/unit_tests/assets/html/ensoft/ensoft.css
Normal file
621
bin/unit_tests/assets/html/ensoft/ensoft.css
Normal file
@@ -0,0 +1,621 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Lato);
|
||||
@import "../anim.css";
|
||||
a:active,
|
||||
a:focus {
|
||||
outline: none;
|
||||
}
|
||||
.no_selection {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.blue_links a {
|
||||
text-decoration: none;
|
||||
color: #4183C4;
|
||||
}
|
||||
.blue_links a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
body {
|
||||
font-family: 'Lato', Helvetica, sans-serif;
|
||||
font-size: 16px;
|
||||
background-color: #ffffff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
::-moz-selection {
|
||||
background-color: #d00000;
|
||||
color: #fff;
|
||||
}
|
||||
::selection {
|
||||
background-color: #d00000;
|
||||
color: #fff;
|
||||
}
|
||||
input,
|
||||
button,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
}
|
||||
#bar.smallscreen {
|
||||
position: absolute !important;
|
||||
width: 200px !important;
|
||||
}
|
||||
#bar {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
width: 250px;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
#bar #logo {
|
||||
margin: 40px 0px 0px 50px;
|
||||
height: 144px;
|
||||
width: 100px;
|
||||
background: url('logo.png') no-repeat center;
|
||||
}
|
||||
#bar #logo #logo-fx {
|
||||
position: absolute;
|
||||
top: -27px;
|
||||
left: -14px;
|
||||
height: 225px;
|
||||
width: 225px;
|
||||
background: url('fx.png') no-repeat center;
|
||||
opacity: 0;
|
||||
-ms-filter: "alpha(opacity=0)";
|
||||
-khtml-opacity: 0;
|
||||
-webkit-animation-name: contract;
|
||||
-moz-animation-name: contract;
|
||||
-webkit-animation-duration: 6s;
|
||||
-moz-animation-duration: 6s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-moz-animation-timing-function: linear;
|
||||
}
|
||||
#bar #links li {
|
||||
margin: 40px 30px 0px 0px;
|
||||
list-style-type: none;
|
||||
}
|
||||
#bar #links li a {
|
||||
padding: 10px 20px 10px 20px;
|
||||
text-decoration: none;
|
||||
color: #808080;
|
||||
}
|
||||
#bar #links li a:hover {
|
||||
color: red;
|
||||
}
|
||||
#bar #rss {
|
||||
margin-top: 100px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
#bar #rss a {
|
||||
background: url("rss.png") no-repeat 8px center;
|
||||
padding: 5px 10px 5px 24px;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
-khtml-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
behavior: url('https://ensoft.dev/assets/js/PIE.php');
|
||||
background-color: #d0d0d0;
|
||||
color: white;
|
||||
}
|
||||
#bar #rss a:hover {
|
||||
background-color: red;
|
||||
}
|
||||
#content.smallscreen {
|
||||
left: 200px !important;
|
||||
}
|
||||
#content {
|
||||
position: absolute;
|
||||
left: 250px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 0px;
|
||||
}
|
||||
#content >h1 {
|
||||
margin: 50px 0 0 50px;
|
||||
font-size: 35px;
|
||||
font-family: 'Lato';
|
||||
font-weight: 900;
|
||||
padding: 0;
|
||||
}
|
||||
a img {
|
||||
border: 0;
|
||||
}
|
||||
#ajax-target {
|
||||
position: relative;
|
||||
}
|
||||
.loading_corner {
|
||||
position: fixed;
|
||||
bottom: 90px;
|
||||
right: 0px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: url("loading.gif?v=3") no-repeat !important;
|
||||
}
|
||||
.kajax-loading .kajax-loading-image {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: url("loading.gif?v=3") no-repeat center white;
|
||||
}
|
||||
#content > .kajax-loading {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 260px;
|
||||
}
|
||||
.stylish-form > .kajax-loading {
|
||||
position: absolute;
|
||||
bottom: 45px;
|
||||
right: 190px;
|
||||
}
|
||||
.blend_input {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
resize: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
.admin-editor {
|
||||
width: 800px;
|
||||
margin-left: 100px;
|
||||
position: relative;
|
||||
}
|
||||
.admin-editor .title {
|
||||
color: #808080;
|
||||
width: 800px;
|
||||
font-size: 40px;
|
||||
height: 40px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
resize: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
.admin-editor .body {
|
||||
color: #808080;
|
||||
padding-left: 60px;
|
||||
margin-left: -50px;
|
||||
background: url('markdown.png') no-repeat top left !important;
|
||||
margin-top: 40px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
resize: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
margin-bottom: 200px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-size: inherit;
|
||||
}
|
||||
.square-link {
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 15px;
|
||||
margin: 14px 5px 14px 0;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.square-button {
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 15px;
|
||||
margin: 14px 5px 14px 0;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background-color: black;
|
||||
border-radius: 5px;
|
||||
-khtml-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
behavior: url('https://ensoft.dev/assets/js/PIE.php');
|
||||
font-size: 16px;
|
||||
color: white !important;
|
||||
}
|
||||
.square-button:hover {
|
||||
background-color: red;
|
||||
}
|
||||
#admin-bar {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
height: 76px;
|
||||
width: 100%;
|
||||
background-color: #202020;
|
||||
z-index: 2;
|
||||
}
|
||||
#admin-bar .container {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#admin-bar .container .left {
|
||||
float: left;
|
||||
}
|
||||
#admin-bar .container .right {
|
||||
float: right;
|
||||
}
|
||||
#admin-bar .container span {
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 15px;
|
||||
margin: 14px 5px 14px 0;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
#admin-bar .container a,
|
||||
#admin-bar .container input[type=submit] {
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 15px;
|
||||
margin: 14px 5px 14px 0;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background-color: black;
|
||||
border-radius: 5px;
|
||||
-khtml-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
behavior: url('https://ensoft.dev/assets/js/PIE.php');
|
||||
font-size: 16px;
|
||||
color: white !important;
|
||||
}
|
||||
#admin-bar .container a:hover,
|
||||
#admin-bar .container input[type=submit]:hover {
|
||||
background-color: red;
|
||||
}
|
||||
#admin-posts {
|
||||
width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#admin-posts h1 {
|
||||
float: left;
|
||||
}
|
||||
#admin-posts .button {
|
||||
float: right !important;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
margin-top: 26px;
|
||||
}
|
||||
#admin-posts ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
clear: both;
|
||||
}
|
||||
#admin-posts li {
|
||||
position: relative;
|
||||
list-style-type: none;
|
||||
padding: 18px 0 20px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
#admin-posts li a {
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
#admin-posts li a:hover {
|
||||
color: red;
|
||||
}
|
||||
#admin-posts li span {
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
#admin-posts li span a {
|
||||
color: #a0a0a0;
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#admin-posts li em {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
font-size: 8px;
|
||||
color: #a0a0a0;
|
||||
}
|
||||
#admin-posts .post_block {
|
||||
float: left;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
}
|
||||
#admin-posts #drafts {
|
||||
float: left;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
border: 4px solid black;
|
||||
border-top: none;
|
||||
}
|
||||
#admin-posts #published {
|
||||
float: left;
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
color: #a0a0a0;
|
||||
}
|
||||
#admin-posts #logout {
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
clear: both;
|
||||
font-size: 14px;
|
||||
margin: 5px 36px -50px 0;
|
||||
}
|
||||
#admin-posts #logout a {
|
||||
text-decoration: none;
|
||||
color: #4183C4;
|
||||
}
|
||||
#admin-posts #logout a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#admin-posts #logout a {
|
||||
color: #505050 !important;
|
||||
}
|
||||
.big_title {
|
||||
margin: 50px 0 0 50px;
|
||||
font-size: 35px;
|
||||
font-family: 'Lato';
|
||||
font-weight: 900;
|
||||
padding: 0;
|
||||
}
|
||||
.blog_post {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
.blog_post .date {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding: 25px 0 25px 50px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.blog_post >h1 {
|
||||
margin: 50px 0 0 50px;
|
||||
font-size: 35px;
|
||||
font-family: 'Lato';
|
||||
font-weight: 900;
|
||||
padding: 0;
|
||||
}
|
||||
.blog_post >h1 >a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.blog_post >h1 >a:hover {
|
||||
color: red;
|
||||
}
|
||||
.blog_post .markdown {
|
||||
width: 500px;
|
||||
margin: 30px 0 65px 50px;
|
||||
color: #808080;
|
||||
}
|
||||
.blog_post .markdown h1,
|
||||
.blog_post .markdown h2,
|
||||
.blog_post .markdown h3,
|
||||
.blog_post .markdown h4,
|
||||
.blog_post .markdown h5 {
|
||||
color: black;
|
||||
}
|
||||
.blog_post .markdown li {
|
||||
line-height: 2.0em;
|
||||
}
|
||||
.blog_post .markdown a {
|
||||
text-decoration: none;
|
||||
color: #4183C4;
|
||||
}
|
||||
.blog_post .markdown a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.blog_post .markdown img {
|
||||
border: 5px solid #f0f0f0;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
-khtml-border-radius: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
behavior: url('https://ensoft.dev/assets/js/PIE.php');
|
||||
}
|
||||
.blog_post .markdown blockquote {
|
||||
border-left: 5px solid #505050;
|
||||
padding-left: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
.blog_post .markdown .footnotes {
|
||||
margin-top: 50px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.blog_post .markdown .footnotes hr {
|
||||
color: #e0e0e0;
|
||||
background-color: #e0e0e0;
|
||||
border: none;
|
||||
height: 1px;
|
||||
}
|
||||
.padded {
|
||||
padding: 50px 0 0 50px;
|
||||
}
|
||||
.styled-box {
|
||||
border: 5px solid black;
|
||||
width: 440px;
|
||||
margin: 50px 0 0 50px;
|
||||
padding: 0px;
|
||||
}
|
||||
.stylish-form {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.stylish-form .mail_icon {
|
||||
background: url('mail.png') no-repeat center right;
|
||||
}
|
||||
.stylish-form .robot_icon {
|
||||
background: url('robot.png') no-repeat center right;
|
||||
}
|
||||
.stylish-form p {
|
||||
color: #808080;
|
||||
font-size: 18px;
|
||||
}
|
||||
.stylish-form li {
|
||||
list-style-type: none;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
.stylish-form input[type=password],
|
||||
.stylish-form input[type=text],
|
||||
.stylish-form textarea {
|
||||
font-size: 22px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
resize: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.stylish-form input[type=password]:focus,
|
||||
.stylish-form input[type=text]:focus,
|
||||
.stylish-form textarea:focus {
|
||||
border-left: 4px solid red;
|
||||
}
|
||||
.stylish-form input[type=submit] {
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 15px;
|
||||
margin: 14px 5px 14px 0;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background-color: black;
|
||||
border-radius: 5px;
|
||||
-khtml-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
behavior: url('https://ensoft.dev/assets/js/PIE.php');
|
||||
font-size: 16px;
|
||||
color: white !important;
|
||||
float: none;
|
||||
}
|
||||
.stylish-form input[type=submit]:hover {
|
||||
background-color: red;
|
||||
}
|
||||
#check-mail-target {
|
||||
display: none;
|
||||
text-align: right;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
}
|
||||
#check-mail-target a {
|
||||
text-decoration: none;
|
||||
color: #4183C4;
|
||||
}
|
||||
#check-mail-target a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.placeholder {
|
||||
color: #808080;
|
||||
}
|
||||
.form-error {
|
||||
padding: 5px !important;
|
||||
display: none;
|
||||
background-color: #ffffb4;
|
||||
}
|
||||
.form-error p {
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
.form-error p:first-child {
|
||||
display: block !important;
|
||||
}
|
||||
#save-success-msg {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: 10px;
|
||||
height: 96px;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
display: none;
|
||||
padding: 5px !important;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
background-color: red;
|
||||
border-radius: 5px;
|
||||
-khtml-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
behavior: url('https://ensoft.dev/assets/js/PIE.php');
|
||||
}
|
||||
#minimap {
|
||||
display: none;
|
||||
opacity: 0.5;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
width: 120px;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
padding: 20px 0 20px 0;
|
||||
margin: 0;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
#minimap li .mark {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 10px;
|
||||
background-color: #c00000;
|
||||
display: none;
|
||||
}
|
||||
#minimap li .selected {
|
||||
background-color: #606060 !important;
|
||||
}
|
||||
#minimap li a {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
background-color: #b0b0b0;
|
||||
border-right: 0;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
padding-right: 10px;
|
||||
text-align: center;
|
||||
color: #a0a0a0;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
-moz-border-radius-topleft: 4px;
|
||||
-moz-border-radius-bottomleft: 4px;
|
||||
-webkit-border-top-left-radius: 4px;
|
||||
-webkit-border-bottom-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
#minimap li a:hover {
|
||||
background-color: #808080;
|
||||
}
|
||||
286
bin/unit_tests/assets/html/ensoft/ensoft.html
Normal file
286
bin/unit_tests/assets/html/ensoft/ensoft.html
Normal file
@@ -0,0 +1,286 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" xmlns:fb="http://www.facebook.com/2008/fbml"><head>
|
||||
<title>ensoft</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="shortcut icon" href="https://ensoft.dev/assets/images/favicon.ico">
|
||||
<link rel="icon" type="image/x-icon" href="https://ensoft.dev/assets/images/favicon.ico">
|
||||
<link rel="icon" type="image/png" href="https://ensoft.dev/assets/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="https://ensoft.dev/assets/images/favicon-48x48.png" sizes="48x48">
|
||||
<link rel="icon" type="image/png" href="https://ensoft.dev/assets/images/favicon-64x64.png" sizes="64x64">
|
||||
<link rel="alternate" type="application/rss+xml" title="ensoft RSS" href="https://ensoft.dev/rss">
|
||||
<link rel="stylesheet" type="text/css" href="ensoft.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
|
||||
<div class="blog_post" id="pampa-energa-en-tecnpolis">
|
||||
|
||||
<h1>
|
||||
<a name="pampa-energa-en-tecnpolis" href="https://ensoft.dev/blog/pampa-energa-en-tecnpolis">
|
||||
Pampa Energía en Tecnópolis </a>
|
||||
</h1>
|
||||
<div class="markdown"><p>Tuvimos el placer de desarrollar en conjunto con el equipo de <a target="_blank" href="http://www.redkatana.com/es/">Red Katana</a> uno de los juegos principales para el stand de <a target="_blank" href="http://www.pampaenergia.com/">Pampa Energía</a> en su stand de Tecnópolis.</p>
|
||||
|
||||
<p>El slogan de este año es "Energía para Transformar" y justamente este
|
||||
es el punto central de este juego, concientizar a los jugadores acerca
|
||||
de la importancia de ahorrar energía.</p>
|
||||
|
||||
<p><img src="" alt=""></p>
|
||||
|
||||
<p>El juego consiste de varias habitaciones de una casa, donde los
|
||||
jugadores deben encontrar de que manera los personajes están
|
||||
desperdiciando la energía y ayudarlos a ahorrarla.</p>
|
||||
|
||||
<p><img src="" alt=""></p>
|
||||
|
||||
<p>Nuestra labor consistió en el desarrollo completo del juego, para ser utilizado en las pantallas táctiles de dicho stand.</p>
|
||||
|
||||
<p>Stand de pampa energía en tecnópolis:</p>
|
||||
|
||||
<p><img src="" alt=""></p>
|
||||
|
||||
<p>Visitantes jugando:</p>
|
||||
|
||||
<p><img src="" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="blog_post" id="tia-maria-get-behind-the-mask">
|
||||
|
||||
<h1>
|
||||
<a name="tia-maria-get-behind-the-mask" href="https://ensoft.dev/blog/tia-maria-get-behind-the-mask">
|
||||
Tia Maria: Get Behind The Mask </a>
|
||||
</h1>
|
||||
<div class="markdown"><p>Bajo encargo del estudio de diseño <a target="_blank" href="http://www.fpestudio.com/">Four Players Studio</a> desarrollamos para la renombrada marca <a target="_blank" href="http://www.tiamaria.com/">Tia Maria</a> una aplicación para celulares y tablets Android que se utilizaría para presentar la nueva campaña publicitaria de la marca:</p>
|
||||
|
||||
<p><img src="" alt=""></p>
|
||||
|
||||
<p>El software consiste en una serie de videos publicitarios por un lado
|
||||
y por el otro la funcionalidad de escanear código QR, dentro de los
|
||||
cuales reconoce los códigos ganadores e informa los premios a los
|
||||
participantes.</p>
|
||||
|
||||
<p><img src="" alt=""></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="blog_post" id="entropia-engine-">
|
||||
|
||||
<h1>
|
||||
<a name="entropia-engine-" href="https://ensoft.dev/blog/entropia-engine-">
|
||||
Entropia Engine++ </a>
|
||||
</h1>
|
||||
<div class="markdown"><h2>Características</h2>
|
||||
|
||||
<p><a target="_blank" href="http://www.eepp.com.ar/">Entropia Engine++</a>,
|
||||
es un motor para el desarrollo de videojuegos 2D multiplataforma,
|
||||
creado para facilitar la creación y distribución de los mismos, pensado
|
||||
para un rendimiento óptimo en cualquier plataforma en la que sea
|
||||
utilizado.</p>
|
||||
|
||||
<h2>Portabilidad</h2>
|
||||
|
||||
<p>El código del mismo puede ser compilado para una cantidad sustancial
|
||||
de plataformas, de diferente naturaleza sin modificar el código. Por
|
||||
ejemplo el mismo juego, puede ser compilado para Windows, Linux, Mac OS,
|
||||
iOS (iPhone y iPad) y Android (Tablets y SmartPhones).</p>
|
||||
|
||||
<h2>Diferencias</h2>
|
||||
|
||||
<p>Se diferencia principalmente de otros motores de desarrollo de
|
||||
videojuegos 2D comerciales y código abierto es en la facilidad de
|
||||
portabilidad del mismo gracias a su modularidad y extensibilidad, provee
|
||||
integramente más módulos de los que ofrecen otras alternativas, entre
|
||||
ellos el módulo de interfaz gráfica, que reduce los tiempos de
|
||||
desarrollo en gran medida. Por otro lado, nuestra elección fue trabajar
|
||||
con un lenguaje compilado y nativo para las plataformas, para poder
|
||||
aprovechar al máximo el hardware de cada una de estas, pudiendo tener
|
||||
control total del manejo de la memoria, y teniendo un rendimiento óptimo
|
||||
en todas las plataformas.</p>
|
||||
|
||||
<h2>Desarrollo</h2>
|
||||
|
||||
<p>El motor se encuentra en activo desarrollo y mantenido por uno de los
|
||||
miembros del equipo, se planifica expandir el mismo de acuerdo a las
|
||||
necesidades que surjan para el desarrollo del videojuego ( entre ellas,
|
||||
completar el portado del mismo a las plataformas móbiles, módulo de
|
||||
networking, módulo de scripting ).</p>
|
||||
|
||||
<h2>Licencia</h2>
|
||||
|
||||
<p>El motor es de código abierto, bajo la licencia MIT<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>, lo que permite utilizarlo libremente tanto para aplicaciones open source como para aplicaciones comerciales.</p>
|
||||
|
||||
<h2>Imágenes</h2>
|
||||
|
||||
<p>Demo de Interfaz Gráfica
|
||||
<a target="_blank" href="https://ensoft.dev/assets/blog/eepp-ui.png" title="Demo de Interfaz Gráfica"><img src="" alt="Demo de Interfaz Gráfica"></a></p>
|
||||
|
||||
<p>Editor de Mapas
|
||||
<a target="_blank" href="https://ensoft.dev/assets/blog/eepp-mapeditor.png" title="Editor de Mapas"><img src="" alt="Editor de Mapas"></a></p>
|
||||
|
||||
<p>Corriendo en HaikuOS<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>
|
||||
<a target="_blank" href="https://ensoft.dev/assets/blog/eepp-haikuos.png" title="Corriendo en HaikuOS"><img src="" alt="Corriendo en HaikuOS"></a> </p>
|
||||
|
||||
<h2>Links</h2>
|
||||
|
||||
<p><a target="_blank" href="http://code.google.com/p/eepp/">Repositório en Google Code</a></p>
|
||||
|
||||
<p><a target="_blank" href="https://ensoft.dev/blog/especificaciones-de-entropia-engine-">Especificaciones Técnicas</a></p>
|
||||
|
||||
<div class="footnotes">
|
||||
<hr>
|
||||
<ol>
|
||||
|
||||
<li id="fn:1">
|
||||
<p><a target="_blank" href="http://es.wikipedia.org/wiki/MIT_License">Licencia MIT en Wikipedia</a> <a href="#fnref:1" rev="footnote">↩</a></p>
|
||||
</li>
|
||||
|
||||
<li id="fn:2">
|
||||
<p><a target="_blank" href="http://haiku-os.org/">HaikuOS</a> <a href="#fnref:2" rev="footnote">↩</a></p>
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="blog_post" id="red-domo">
|
||||
|
||||
<h1>
|
||||
<a name="red-domo" href="https://ensoft.dev/blog/red-domo">
|
||||
Red Domo </a>
|
||||
</h1>
|
||||
<div class="markdown"><p>Tuvimos el placer de trabajar con la gente de <a target="_blank" href="http://deitres.com.ar/">Deitres</a>,
|
||||
una empresa Marplatense de alcance nacional que utiliza tecnología
|
||||
inalámbrica de vanguardia en sístemas de monitoreo de alarmas.</p>
|
||||
|
||||
<p>El trabajo realizado consiste en un ambicioso proyecto que permite
|
||||
que su sistema de alarmas esté completamente integrado y pueda ser
|
||||
accedido vía web, utilizando un navegador web estándar.</p>
|
||||
|
||||
<p>Fué todo un desafío para nosotros debido a la infraestuctura que ya
|
||||
tenía en funcionamiento la empresa y las características de seguridad
|
||||
que requiere un sistema con estas características.</p>
|
||||
|
||||
<p>Después de muchas reuniones, y de un planeamiento cuidadoso comenzamos a desarrollar lo que hoy es <a target="_blank" href="https://www.reddomo.com.ar/">DOMO</a>.</p>
|
||||
|
||||
<p><img src="" alt="Domo"></p>
|
||||
|
||||
<h2>Naturaleza del Proyecto</h2>
|
||||
|
||||
<p>El proyecto fué dividido en tres partes<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>:</p>
|
||||
|
||||
<h3>Receptora</h3>
|
||||
|
||||
<p>La receptora es la encargarda de recibir todos los datos que llegan de los diferentes sistemas de alarmas.
|
||||
El desarrollo se llevó a cabo usando <em>C#</em> y partiendo de base de una librería interna proveída por el departamento técnico de Deitres.
|
||||
Fué planeada con tolerancia a fallos y problemas de conexión.
|
||||
Ésta luego de procesar los paquetes los envia por un canal seguro hacia el servidor.</p>
|
||||
|
||||
<h3>Servidor</h3>
|
||||
|
||||
<p>El servidor fué desarrollado integramente en <em>PHP</em>, utilizando un <em>Servidor Dedicado</em>.
|
||||
Su principal función es la de recibir los datos que envía la receptora,
|
||||
clasificarlos de forma correspondiente y asociarlos a las cuentas de
|
||||
usuario y diferentes proveedoras de alarmas distribuidas por todo el
|
||||
país.</p>
|
||||
|
||||
<h3>Página Web.</h3>
|
||||
|
||||
<p>La página Web, a su vez está dividida en tres partes:</p>
|
||||
|
||||
<ul>
|
||||
<li>Cuenta de Administrador.</li>
|
||||
<li>Cuentas para proveedores de servicios de alarmas.</li>
|
||||
<li>Cuentas para usuarios finales.</li>
|
||||
</ul>
|
||||
|
||||
<p>Fué desarrollada<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup> utilizando <em>PHP</em> y <em>Javascript</em>, haciendo uso intensivo de el servicio de <em>Google Maps</em> para visualizar en tiempo real el estado de todos los equipos de alarmas conectados al sistema.</p>
|
||||
|
||||
<p>Toda las transacciones entre el navegador y el servidor son realizadas a través de una conexión segura utilizando el protocolo <strong>SSL</strong>..</p>
|
||||
|
||||
<h2>Funcionalidad</h2>
|
||||
|
||||
<p>El sistema completo provee una integración total de los servicios ya
|
||||
ofrecidos por la empresa, mostrando gráficos en tiempo real del
|
||||
rendimiento de los equipos. Ubicando en un mapa todos los puntos donde
|
||||
se encuentran las alarmas y permitiendo administrar las cuentas de
|
||||
usuario y las alertas recibidas.</p>
|
||||
|
||||
<p><img src="" alt="Domo"></p>
|
||||
|
||||
<p>Además para el usuario final le permite acceder, cualquiera sea su
|
||||
ubicación al estado de las diferentes alarmas que posee intaladas,
|
||||
permitiendole ver el estado y los mensajes que ha enviado la alarma al
|
||||
sistema.</p>
|
||||
|
||||
<p>Cabe destacar también que los usuarios que tienen contratado el servicio de <strong>domótica</strong>
|
||||
pueden controlar los diferentes dispositivos conectados a las alarmas,
|
||||
por ejemplo, pueden abrir y cerrar puertas, encender luces controlar
|
||||
calderas, etc. Todo desde su navegador web.</p>
|
||||
|
||||
<h2>Palabras Finales</h2>
|
||||
|
||||
<p>La implementación principal del proyecto llevó alrededor de cuatro meses de trabajo con colaboración constante entre las partes.</p>
|
||||
|
||||
<p>Hemos formado una sólida relación de negocios con dicha empresa y al
|
||||
día de hoy continuamos trabajando en agregar funcionalidad al sistema, y
|
||||
siempre dispuestos a innovar de forma conjunta.</p>
|
||||
|
||||
<div class="footnotes">
|
||||
<hr>
|
||||
<ol>
|
||||
|
||||
<li id="fn:1">
|
||||
<p>Debido a la discreción requerida en este tipo de proyectos, se mencionan superficialmente las características del mismo. <a href="#fnref:1" rev="footnote">↩</a></p>
|
||||
</li>
|
||||
|
||||
<li id="fn:2">
|
||||
<p>El diseño gráfico de la misma fué contratado externamente por Deitres y es una realización de <a target="_blank" href="http://chic-creative.com/">Chic Creative</a>. <a href="#fnref:2" rev="footnote">↩</a></p>
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="blog_post" id="todo-deportes">
|
||||
|
||||
<h1>
|
||||
<a name="todo-deportes" href="https://ensoft.dev/blog/todo-deportes">
|
||||
Todo Deportes </a>
|
||||
</h1>
|
||||
<div class="markdown"><p>La web de <a target="_blank" href="http://www.tododeportesmdq.com.ar/nosotros">Todo Deportes</a> fué diseñada para una empresa que comercializa insumos para <strong>deportes extremos</strong>.</p>
|
||||
|
||||
<p>Se mantuvo un estilo simple, en tonos amarillos y negros acorde al logo de la empresa.</p>
|
||||
|
||||
<p>El sitio posee una galería interactiva en la parte superior de la
|
||||
página, una navegación fluida y cuatro secciones: la página de inicio,
|
||||
las marcas ofrecidas, el team sponsoreado y un formulario de contacto.</p>
|
||||
|
||||
<p><img src="" alt="Todo Deportes"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="bar"><a href="https://ensoft.dev/"><div id="logo"><div id="logo-fx"></div></div></a>
|
||||
|
||||
|
||||
<ul id="links">
|
||||
<li><a class="ajax-link" href="https://ensoft.dev/portfolio">portfolio</a></li>
|
||||
<li><a class="ajax-link" href="https://ensoft.dev/nosotros">nosotros</a></li>
|
||||
<!--<li><a class="ajax-link" href="https://ensoft.dev/blog">blog</a></li>-->
|
||||
<li><a class="ajax-link" href="https://ensoft.dev/contacto">contacto</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div id="rss"><a href="https://ensoft.dev/rss">RSS</a></div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
BIN
bin/unit_tests/assets/html/ensoft/fx.png
Normal file
BIN
bin/unit_tests/assets/html/ensoft/fx.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
bin/unit_tests/assets/html/ensoft/logo.png
Normal file
BIN
bin/unit_tests/assets/html/ensoft/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
BIN
bin/unit_tests/assets/html/ensoft/rss.png
Normal file
BIN
bin/unit_tests/assets/html/ensoft/rss.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -31,21 +31,27 @@ class EE_API UIHTMLWidget : public UILayout {
|
||||
virtual void onDisplayChange();
|
||||
|
||||
CSSDisplay getDisplay() const { return mDisplay; }
|
||||
|
||||
void setDisplay( CSSDisplay display );
|
||||
|
||||
CSSPosition getCSSPosition() const { return mPosition; }
|
||||
|
||||
void setCSSPosition( CSSPosition position );
|
||||
|
||||
CSSFloat getCSSFloat() const { return mFloat; }
|
||||
|
||||
void setCSSFloat( CSSFloat cssFloat );
|
||||
|
||||
CSSClear getCSSClear() const { return mClear; }
|
||||
|
||||
void setCSSClear( CSSClear cssClear );
|
||||
|
||||
const Rectf& getOffsets() const { return mOffsets; }
|
||||
|
||||
void setOffsets( const Rectf& offsets );
|
||||
|
||||
int getZIndex() const { return mZIndex; }
|
||||
|
||||
void setZIndex( int zIndex );
|
||||
|
||||
virtual std::vector<PropertyId> getPropertiesImplemented() const;
|
||||
@@ -57,10 +63,18 @@ class EE_API UIHTMLWidget : public UILayout {
|
||||
|
||||
virtual void updateLayout();
|
||||
|
||||
virtual void onParentChange();
|
||||
|
||||
virtual void onPositionChange();
|
||||
|
||||
UIWidget* getContainingBlock();
|
||||
|
||||
void positionOutOfFlowChildren();
|
||||
|
||||
void updateOutOfFlowPosition();
|
||||
|
||||
void updateStickyPosition();
|
||||
|
||||
virtual RichText* getRichTextPtr() { return nullptr; }
|
||||
|
||||
virtual bool isMergeable() const { return false; }
|
||||
@@ -84,6 +98,14 @@ class EE_API UIHTMLWidget : public UILayout {
|
||||
int mZIndex{ 0 };
|
||||
UILayouter* mLayouter{ nullptr };
|
||||
UnorderedMap<std::string, StyleSheetProperty> mDataProperties;
|
||||
|
||||
Uint32 mScrollCb{ 0 };
|
||||
Node* mScrollTarget{ nullptr };
|
||||
Vector2f mStickyBasePos;
|
||||
bool mIsUpdatingScroll{ false };
|
||||
|
||||
void updateScrollListeners();
|
||||
void onScrollTargetPositionChange();
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -178,6 +178,7 @@ class EE_API UIHTMLBody : public UIRichText {
|
||||
virtual Uint32 getType() const override;
|
||||
bool isType( const Uint32& type ) const override;
|
||||
bool applyProperty( const StyleSheetProperty& attribute ) override;
|
||||
virtual void updateLayout() override;
|
||||
|
||||
protected:
|
||||
bool mPropagatedBackground{ false };
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <eepp/ui/uihtmlwidget.hpp>
|
||||
#include <eepp/ui/uilayouter.hpp>
|
||||
#include <eepp/ui/uilayoutermanager.hpp>
|
||||
#include <eepp/ui/uiscrollablewidget.hpp>
|
||||
#include <eepp/ui/uiscrollview.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
@@ -12,6 +14,8 @@ UIHTMLWidget* UIHTMLWidget::New() {
|
||||
UIHTMLWidget::UIHTMLWidget( const std::string& tag ) : UILayout( tag ) {}
|
||||
|
||||
UIHTMLWidget::~UIHTMLWidget() {
|
||||
if ( mScrollTarget && mScrollCb )
|
||||
mScrollTarget->removeEventListener( mScrollCb );
|
||||
eeSAFE_DELETE( mLayouter );
|
||||
}
|
||||
|
||||
@@ -65,6 +69,7 @@ void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
|
||||
if ( getLayoutWidthPolicy() == SizePolicy::MatchParent )
|
||||
setLayoutWidthPolicy( SizePolicy::WrapContent );
|
||||
}
|
||||
updateScrollListeners();
|
||||
onPositionChange();
|
||||
}
|
||||
}
|
||||
@@ -201,17 +206,22 @@ void UIHTMLWidget::updateLayout() {
|
||||
UIWidget* UIHTMLWidget::getContainingBlock() {
|
||||
if ( mPosition == CSSPosition::Fixed ) {
|
||||
Node* parent = getParent();
|
||||
UIWidget* lastWidget = parent && parent->isWidget() ? parent->asType<UIWidget>() : nullptr;
|
||||
UIWidget* cb = parent && parent->isWidget() ? parent->asType<UIWidget>() : nullptr;
|
||||
while ( parent ) {
|
||||
if ( parent->isType( UI_TYPE_SCROLLVIEW ) ) {
|
||||
cb = parent->asType<UIWidget>();
|
||||
break;
|
||||
}
|
||||
if ( parent->isWidget() )
|
||||
lastWidget = parent->asType<UIWidget>();
|
||||
cb = parent->asType<UIWidget>();
|
||||
parent = parent->getParent();
|
||||
}
|
||||
return lastWidget;
|
||||
return cb;
|
||||
}
|
||||
|
||||
Node* parent = getParent();
|
||||
UIWidget* lastWidget = nullptr;
|
||||
UIWidget* htmlWidget = nullptr;
|
||||
while ( parent ) {
|
||||
if ( parent->isWidget() ) {
|
||||
lastWidget = parent->asType<UIWidget>();
|
||||
@@ -219,11 +229,14 @@ UIWidget* UIHTMLWidget::getContainingBlock() {
|
||||
if ( lastWidget->asType<UIHTMLWidget>()->getCSSPosition() != CSSPosition::Static ) {
|
||||
return lastWidget;
|
||||
}
|
||||
if ( lastWidget->isType( UI_TYPE_HTML_HTML ) ) {
|
||||
htmlWidget = lastWidget;
|
||||
}
|
||||
}
|
||||
}
|
||||
parent = parent->getParent();
|
||||
}
|
||||
return lastWidget;
|
||||
return htmlWidget ? htmlWidget : lastWidget;
|
||||
}
|
||||
|
||||
void UIHTMLWidget::positionOutOfFlowChildren() {
|
||||
@@ -233,66 +246,182 @@ void UIHTMLWidget::positionOutOfFlowChildren() {
|
||||
UIHTMLWidget* htmlChild = static_cast<UIHTMLWidget*>( child );
|
||||
CSSPosition pos = htmlChild->getCSSPosition();
|
||||
if ( pos == CSSPosition::Absolute || pos == CSSPosition::Fixed ) {
|
||||
UIWidget* cb = htmlChild->getContainingBlock();
|
||||
if ( cb ) {
|
||||
Rectf cbContentOffset = cb->getPixelsContentOffset();
|
||||
Float cbContentWidth = cb->getPixelsSize().getWidth() - cbContentOffset.Left -
|
||||
cbContentOffset.Right;
|
||||
Float cbContentHeight = cb->getPixelsSize().getHeight() - cbContentOffset.Top -
|
||||
cbContentOffset.Bottom;
|
||||
|
||||
Rectf margin = htmlChild->getLayoutPixelsMargin();
|
||||
Float childWidth = htmlChild->getPixelsSize().getWidth();
|
||||
Float childHeight = htmlChild->getPixelsSize().getHeight();
|
||||
|
||||
Float top = 0;
|
||||
Float left = 0;
|
||||
|
||||
bool useTop = htmlChild->mTopEq != "auto";
|
||||
bool useBottom = htmlChild->mBottomEq != "auto";
|
||||
bool useLeft = htmlChild->mLeftEq != "auto";
|
||||
bool useRight = htmlChild->mRightEq != "auto";
|
||||
|
||||
if ( useLeft ) {
|
||||
left = htmlChild->lengthFromValue(
|
||||
htmlChild->mLeftEq, CSS::PropertyRelativeTarget::ContainingBlockWidth,
|
||||
0 );
|
||||
} else if ( useRight ) {
|
||||
Float rightVal = htmlChild->lengthFromValue(
|
||||
htmlChild->mRightEq, CSS::PropertyRelativeTarget::ContainingBlockWidth,
|
||||
0 );
|
||||
left = cbContentWidth - childWidth - margin.Left - margin.Right - rightVal;
|
||||
}
|
||||
|
||||
if ( useTop ) {
|
||||
top = htmlChild->lengthFromValue(
|
||||
htmlChild->mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight,
|
||||
0 );
|
||||
} else if ( useBottom ) {
|
||||
Float bottomVal = htmlChild->lengthFromValue(
|
||||
htmlChild->mBottomEq,
|
||||
CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
|
||||
top =
|
||||
cbContentHeight - childHeight - margin.Top - margin.Bottom - bottomVal;
|
||||
}
|
||||
|
||||
top += margin.Top;
|
||||
left += margin.Left;
|
||||
|
||||
Vector2f cbPos( cbContentOffset.Left, cbContentOffset.Top );
|
||||
cbPos.x += left;
|
||||
cbPos.y += top;
|
||||
|
||||
Vector2f worldPos = cb->convertToWorldSpace( cbPos );
|
||||
Vector2f localPos = convertToNodeSpace( worldPos );
|
||||
htmlChild->setPixelsPosition( localPos );
|
||||
}
|
||||
htmlChild->updateOutOfFlowPosition();
|
||||
}
|
||||
}
|
||||
child = child->getNextNode();
|
||||
}
|
||||
}
|
||||
|
||||
void UIHTMLWidget::updateOutOfFlowPosition() {
|
||||
UIWidget* cb = getContainingBlock();
|
||||
if ( !cb )
|
||||
return;
|
||||
|
||||
Rectf cbContentOffset = cb->getPixelsContentOffset();
|
||||
Float cbContentWidth =
|
||||
cb->getPixelsSize().getWidth() - cbContentOffset.Left - cbContentOffset.Right;
|
||||
Float cbContentHeight =
|
||||
cb->getPixelsSize().getHeight() - cbContentOffset.Top - cbContentOffset.Bottom;
|
||||
|
||||
Rectf margin = getLayoutPixelsMargin();
|
||||
Float childWidth = getPixelsSize().getWidth();
|
||||
Float childHeight = getPixelsSize().getHeight();
|
||||
|
||||
Float top = 0;
|
||||
Float left = 0;
|
||||
|
||||
bool useTop = mTopEq != "auto";
|
||||
bool useBottom = mBottomEq != "auto";
|
||||
bool useLeft = mLeftEq != "auto";
|
||||
bool useRight = mRightEq != "auto";
|
||||
|
||||
if ( useLeft ) {
|
||||
left = lengthFromValue( mLeftEq, CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
|
||||
} else if ( useRight ) {
|
||||
Float rightVal =
|
||||
lengthFromValue( mRightEq, CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
|
||||
left = cbContentWidth - childWidth - margin.Left - margin.Right - rightVal;
|
||||
}
|
||||
|
||||
if ( useTop ) {
|
||||
top = lengthFromValue( mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
|
||||
} else if ( useBottom ) {
|
||||
Float bottomVal =
|
||||
lengthFromValue( mBottomEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
|
||||
top = cbContentHeight - childHeight - margin.Top - margin.Bottom - bottomVal;
|
||||
}
|
||||
|
||||
top += margin.Top;
|
||||
left += margin.Left;
|
||||
|
||||
Vector2f cbPos( cbContentOffset.Left, cbContentOffset.Top );
|
||||
cbPos.x += left;
|
||||
cbPos.y += top;
|
||||
|
||||
Vector2f worldPos = cb->convertToWorldSpace( cbPos );
|
||||
Vector2f localPos = getParent()->convertToNodeSpace( worldPos );
|
||||
setPixelsPosition( localPos );
|
||||
}
|
||||
|
||||
void UIHTMLWidget::updateStickyPosition() {
|
||||
if ( !mScrollTarget )
|
||||
return;
|
||||
|
||||
UIWidget* cb = getContainingBlock();
|
||||
if ( !cb )
|
||||
return;
|
||||
|
||||
Vector2f baseWorldPos = getParent()->convertToWorldSpace( mStickyBasePos );
|
||||
|
||||
Node* viewport = mScrollTarget->getParent();
|
||||
if ( !viewport )
|
||||
return;
|
||||
|
||||
Vector2f posInViewport = viewport->convertToNodeSpace( baseWorldPos );
|
||||
|
||||
Float topOffset = 0;
|
||||
bool useTop = mTopEq != "auto";
|
||||
if ( useTop )
|
||||
topOffset =
|
||||
lengthFromValue( mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
|
||||
|
||||
Float bottomOffset = 0;
|
||||
bool useBottom = mBottomEq != "auto";
|
||||
if ( useBottom )
|
||||
bottomOffset =
|
||||
lengthFromValue( mBottomEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
|
||||
|
||||
Vector2f newPosInViewport = posInViewport;
|
||||
|
||||
if ( useTop ) {
|
||||
if ( posInViewport.y < topOffset ) {
|
||||
newPosInViewport.y = topOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if ( useBottom ) {
|
||||
Float viewportHeight = viewport->getSize().getHeight();
|
||||
if ( posInViewport.y + getPixelsSize().getHeight() > viewportHeight - bottomOffset ) {
|
||||
newPosInViewport.y = viewportHeight - bottomOffset - getPixelsSize().getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2f cbWorldPos = cb->convertToWorldSpace( Vector2f( 0, 0 ) );
|
||||
Vector2f cbInViewport = viewport->convertToNodeSpace( cbWorldPos );
|
||||
Float cbBottomInViewport =
|
||||
cbInViewport.y + cb->getPixelsSize().getHeight() - cb->getPixelsPadding().Bottom;
|
||||
|
||||
if ( newPosInViewport.y + getPixelsSize().getHeight() > cbBottomInViewport ) {
|
||||
newPosInViewport.y = cbBottomInViewport - getPixelsSize().getHeight();
|
||||
}
|
||||
|
||||
if ( newPosInViewport.y < cbInViewport.y + cb->getPixelsPadding().Top ) {
|
||||
newPosInViewport.y = cbInViewport.y + cb->getPixelsPadding().Top;
|
||||
}
|
||||
|
||||
if ( newPosInViewport != posInViewport ) {
|
||||
Vector2f newWorldPos = viewport->convertToWorldSpace( newPosInViewport );
|
||||
Vector2f newLocalPos = getParent()->convertToNodeSpace( newWorldPos );
|
||||
|
||||
mIsUpdatingScroll = true;
|
||||
setPixelsPosition( newLocalPos );
|
||||
mIsUpdatingScroll = false;
|
||||
} else {
|
||||
mIsUpdatingScroll = true;
|
||||
setPixelsPosition( mStickyBasePos );
|
||||
mIsUpdatingScroll = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UIHTMLWidget::updateScrollListeners() {
|
||||
if ( mScrollTarget ) {
|
||||
if ( mScrollCb ) {
|
||||
mScrollTarget->removeEventListener( mScrollCb );
|
||||
mScrollCb = 0;
|
||||
}
|
||||
mScrollTarget = nullptr;
|
||||
}
|
||||
|
||||
if ( mPosition == CSSPosition::Fixed || mPosition == CSSPosition::Sticky ) {
|
||||
Node* parent = getParent();
|
||||
while ( parent ) {
|
||||
if ( parent->isType( UI_TYPE_SCROLLVIEW ) ) {
|
||||
mScrollTarget = parent->asType<UIScrollView>()->getScrollView();
|
||||
break;
|
||||
}
|
||||
parent = parent->getParent();
|
||||
}
|
||||
|
||||
if ( mScrollTarget ) {
|
||||
mScrollCb = mScrollTarget->on( Event::OnPositionChange, [this]( const Event* ) {
|
||||
onScrollTargetPositionChange();
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIHTMLWidget::onParentChange() {
|
||||
UILayout::onParentChange();
|
||||
updateScrollListeners();
|
||||
}
|
||||
|
||||
void UIHTMLWidget::onPositionChange() {
|
||||
UILayout::onPositionChange();
|
||||
if ( mPosition == CSSPosition::Sticky && !mIsUpdatingScroll ) {
|
||||
mStickyBasePos = getPixelsPosition();
|
||||
updateStickyPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void UIHTMLWidget::onScrollTargetPositionChange() {
|
||||
if ( mPosition == CSSPosition::Fixed ) {
|
||||
updateOutOfFlowPosition();
|
||||
} else if ( mPosition == CSSPosition::Sticky ) {
|
||||
updateStickyPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void UIHTMLWidget::invalidateIntrinsicSize() {
|
||||
if ( mLayouter )
|
||||
mLayouter->invalidateIntrinsicWidths();
|
||||
|
||||
@@ -112,6 +112,36 @@ bool UIHTMLBody::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
return UIRichText::applyProperty( attribute );
|
||||
}
|
||||
|
||||
void UIHTMLBody::updateLayout() {
|
||||
UIRichText::updateLayout();
|
||||
|
||||
if ( mChild && mChild->isWidget() ) {
|
||||
Float maxH = 0;
|
||||
Node* child = mChild;
|
||||
while ( child ) {
|
||||
if ( child->isWidget() ) {
|
||||
UIWidget* widget = child->asType<UIWidget>();
|
||||
bool isFixed = false;
|
||||
if ( widget->isType( UI_TYPE_HTML_WIDGET ) &&
|
||||
widget->asType<UIHTMLWidget>()->getCSSPosition() == CSSPosition::Fixed ) {
|
||||
isFixed = true;
|
||||
}
|
||||
if ( !isFixed ) {
|
||||
Float childH =
|
||||
widget->getPixelsPosition().y + widget->getPixelsSize().getHeight();
|
||||
maxH = std::max( maxH, childH );
|
||||
}
|
||||
}
|
||||
child = child->getNextNode();
|
||||
}
|
||||
if ( maxH > 0 ) {
|
||||
Float dpH = PixelDensity::pxToDp( maxH );
|
||||
if ( dpH != getMinSize().getHeight() )
|
||||
setMinHeight( dpH );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UIRichText* UIRichText::NewHtml() {
|
||||
auto* html = UIHTMLHtml::New( "html" );
|
||||
html->setClipType( ClipType::None );
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <eepp/ui/uihtmlwidget.hpp>
|
||||
#include <eepp/ui/uirichtext.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uiscrollbar.hpp>
|
||||
#include <eepp/ui/uiscrollview.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
#include <eepp/window/window.hpp>
|
||||
@@ -351,3 +353,88 @@ UTEST( UIHTMLWidget, positionOutOfFlow_RightBottomWithMargin ) {
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTMLWidget, positionOutOfFlow_FixedScroll ) {
|
||||
init_ui_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
UIScrollView* scrollView = UIScrollView::New();
|
||||
scrollView->setParent( sceneNode->getRoot() );
|
||||
scrollView->setPixelsSize( 800, 600 );
|
||||
|
||||
UIHTMLBody* body = UIHTMLBody::New( "body" );
|
||||
body->setParent( scrollView );
|
||||
body->applyProperty( StyleSheetProperty( "position", "relative" ) );
|
||||
|
||||
UIWidget* dummyChild = UIWidget::New();
|
||||
dummyChild->setParent( body );
|
||||
dummyChild->setPixelsSize( 800, 2000 );
|
||||
dummyChild->setPixelsPosition( 0, 0 );
|
||||
|
||||
UIHTMLWidget* fixedChild = UIHTMLWidget::New();
|
||||
fixedChild->setParent( body );
|
||||
fixedChild->setPixelsSize( 100, 50 );
|
||||
fixedChild->applyProperty( StyleSheetProperty( "position", "fixed" ) );
|
||||
fixedChild->applyProperty( StyleSheetProperty( "top", "50px" ) );
|
||||
fixedChild->applyProperty( StyleSheetProperty( "left", "50px" ) );
|
||||
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
// Scroll down by 200px
|
||||
scrollView->getScrollView()->setPosition( { 0, -200 } );
|
||||
|
||||
Vector2f worldPos = fixedChild->convertToWorldSpace( { 0, 0 } );
|
||||
EXPECT_NEAR( 50.f, worldPos.x, 1.f );
|
||||
EXPECT_NEAR( 50.f, worldPos.y, 1.f );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTMLWidget, positionOutOfFlow_StickyScroll ) {
|
||||
init_ui_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
UIScrollView* scrollView = UIScrollView::New();
|
||||
scrollView->setParent( sceneNode->getRoot() );
|
||||
scrollView->setPixelsSize( 800, 600 );
|
||||
|
||||
UIHTMLBody* body = UIHTMLBody::New( "body" );
|
||||
body->setParent( scrollView );
|
||||
body->applyProperty( StyleSheetProperty( "position", "relative" ) );
|
||||
|
||||
UIHTMLWidget* stickyChild = UIHTMLWidget::New();
|
||||
stickyChild->setParent( body );
|
||||
stickyChild->setPixelsSize( 100, 50 );
|
||||
stickyChild->applyProperty( StyleSheetProperty( "margin-top", "100px" ) );
|
||||
stickyChild->applyProperty( StyleSheetProperty( "position", "sticky" ) );
|
||||
stickyChild->applyProperty( StyleSheetProperty( "top", "20px" ) );
|
||||
|
||||
UIWidget* dummyChild = UIWidget::New();
|
||||
dummyChild->setParent( body );
|
||||
dummyChild->setPixelsSize( 800, 2000 );
|
||||
dummyChild->setPixelsPosition( 0, 0 );
|
||||
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
// Force base pos (as if layouter did it)
|
||||
stickyChild->setPixelsPosition( 0, 100 );
|
||||
|
||||
// Ensure base pos was captured correctly
|
||||
EXPECT_NEAR( 100.f, stickyChild->getPixelsPosition().y, 1.f );
|
||||
|
||||
// Scroll down by 50px
|
||||
Float actualMaxScrollY =
|
||||
body->getPixelsSize().getHeight() - scrollView->getContainer()->getPixelsSize().getHeight();
|
||||
scrollView->getVerticalScrollBar()->setValue( 50.f / actualMaxScrollY );
|
||||
|
||||
Vector2f worldPos1 = stickyChild->convertToWorldSpace( { 0, 0 } );
|
||||
EXPECT_NEAR( 50.f, worldPos1.y, 1.f );
|
||||
|
||||
// Scroll down by 150px
|
||||
scrollView->getVerticalScrollBar()->setValue( 150.f / actualMaxScrollY );
|
||||
|
||||
Vector2f worldPos2 = stickyChild->convertToWorldSpace( { 0, 0 } );
|
||||
EXPECT_NEAR( 20.f, worldPos2.y, 1.f );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
@@ -1173,3 +1173,71 @@ UTEST( UIHTML, InlineBlockBrowserTest ) {
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTML, HeightExpansion ) {
|
||||
Engine::instance()->createWindow( WindowSettings( 1024, 653, "Height Expansion Test",
|
||||
WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ),
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
|
||||
UI::UISceneNode* sceneNode = init_test_inline_block();
|
||||
sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/ensoft/" );
|
||||
|
||||
std::string html;
|
||||
FileSystem::fileGet( "assets/html/ensoft/ensoft.html", html );
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
|
||||
// Wait a bit and update again to make sure layouts are computed
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
auto htmlNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_HTML );
|
||||
auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY );
|
||||
|
||||
ASSERT_TRUE( htmlNode != nullptr );
|
||||
ASSERT_TRUE( bodyNode != nullptr );
|
||||
|
||||
auto htmlWidget = htmlNode->asType<UIWidget>();
|
||||
auto bodyWidget = bodyNode->asType<UIWidget>();
|
||||
|
||||
EXPECT_GT( htmlWidget->getSize().getHeight(), 0 );
|
||||
EXPECT_GT( bodyWidget->getSize().getHeight(), 0 );
|
||||
|
||||
EXPECT_GE( htmlWidget->getSize().getHeight(), bodyWidget->getSize().getHeight() );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTML, HeightExpansion_FixedDoesNotExpand ) {
|
||||
Engine::instance()->createWindow( WindowSettings( 1024, 653, "Height Expansion Test",
|
||||
WindowStyle::Default, WindowBackend::Default,
|
||||
32, {}, 1, false, true ),
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
|
||||
UI::UISceneNode* sceneNode = init_test_inline_block();
|
||||
|
||||
const std::string html = R"HTML(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<div style="height: 100px;"></div>
|
||||
<div style="position: fixed; top: 500px; height: 50px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
)HTML";
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
sceneNode->update( Seconds( 1 ) );
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY );
|
||||
ASSERT_TRUE( bodyNode != nullptr );
|
||||
|
||||
auto bodyWidget = bodyNode->asType<UIWidget>();
|
||||
|
||||
// The height should be 100px, not 550px because the fixed div should be ignored.
|
||||
EXPECT_NEAR( bodyWidget->getPixelsSize().getHeight(), 100.f, 1.f );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user